I am currently implementing a library to serialize and deserialize to and from XML-RPC messages. It's almost done but now I am trying to remove the boilerplate of my current asProduct method using Shapeless. My current code:
trait Serializer[T] {
def serialize(value: T): NodeSeq
}
trait Deserializer[T] {
type Deserialized[T] = Validation[AnyErrors, T]
type AnyErrors = NonEmptyList[AnyError]
def deserialize(from: NodeSeq): Deserialized[T]
}
trait Datatype[T] extends Serializer[T] with Deserializer[T]
// Example of asProduct, there are 20 more methods like this, from arity 1 to 22
def asProduct2[S, T1: Datatype, T2: Datatype](apply: (T1, T2) => S)(unapply: S => Product2[T1, T2]) = new Datatype[S] {
override def serialize(value: S): NodeSeq = {
val params = unapply(value)
val b = toXmlrpc(params._1) ++ toXmlrpc(params._2)
b.theSeq
}
// Using scalaz
override def deserialize(from: NodeSeq): Deserialized[S] = (
fromXmlrpc[T1](from(0)) |@| fromXmlrpc[T2](from(1))
) {apply}
}
My goal is to allow the user of my library to serialize/deserialize case classes without force him to write boilerplate code. Currently, you have to declare the case class and an implicit val using the aforementioned asProduct method to have a Datatype instance in context. This implicit is used in the following code:
def toXmlrpc[T](datatype: T)(implicit serializer: Serializer[T]): NodeSeq =
serializer.serialize(datatype)
def fromXmlrpc[T](value: NodeSeq)(implicit deserializer: Deserializer[T]): Deserialized[T] =
deserializer.deserialize(value)
This is the classic strategy of serializing and deserializing using type classes.
At this moment, I have grasped how to convert from case classes to HList via Generic or LabelledGeneric. The problem is once I have this conversion done how I can call the methods fromXmlrpc and toXmlrpc as in the asProduct2 example. I don't have any information about the types of the attributes in the case class and, therefore, the compiler cannot find any implicit that satisfy fromXmlrpc and toXmlrpc. I need a way to constrain that all the elements of a HList have an implicit Datatype in context.
As I am a beginner with Shapeless, I would like to know what's the best way of getting this functionality. I have some insights but I definitely have no idea of how to get it done using Shapeless. The ideal would be to have a way to get the type from a given attribute of the case class and pass this type explicitly to fromXmlrpc and toXmlrpc. I imagine that this is not how it can be done.