Circe instances for encoding/decoding sealed trait instances of arity 0? - scala

I'm using sealed traits as enums for exhaustive pattern matching. In cases where I have case objects instead of case classes extending my trait, I'd like to encode and decode (via Circe) as just a plain string.
For example:
sealed trait State
case object On extends State
case object Off extends State
val a: State = State.Off
a.asJson.noSpaces // trying for "Off"
decode[State]("On") // should be State.On
I understand that this will be configurable in 0.5.0, but can anyone help me write something to tide me over until that's released?

To highlight the problem—assuming this ADT:
sealed trait State
case object On extends State
case object Off extends State
circe's generic derivation will (currently) produce the following encodings:
scala> import io.circe.generic.auto._, io.circe.syntax._
import io.circe.generic.auto._
import io.circe.syntax._
scala> On.asJson.noSpaces
res0: String = {}
scala> (On: State).asJson.noSpaces
res1: String = {"On":{}}
This is because the generic derivation mechanism is built on Shapeless's LabelledGeneric, which represents case objects as empty HLists. This will probably always be the default behavior, since it's clean, simple, and consistent, but it's not always what you want (as you note the configuration options that are coming soon will support alternatives).
You can override this behavior by providing your own generic instances for case objects:
import io.circe.Encoder
import shapeless.{ Generic, HNil }
implicit def encodeCaseObject[A <: Product](implicit
gen: Generic.Aux[A, HNil]
): Encoder[A] = Encoder[String].contramap[A](_.productPrefix)
This says, "if the generic representation of A is an empty HList, encode it as its name as a JSON string". And it works as we'd expect for case objects that are statically typed as themselves:
scala> On.asJson.noSpaces
res2: String = "On"
When the value is statically typed as the base type, the story is a little different:
scala> (On: State).asJson.noSpaces
res3: String = {"On":"On"}
We get a generically derived instance for State, and it respects our manually defined generic instance for case objects, but it still wraps them in an object. This makes some sense if you think about it—the ADT could contain case classes, which can only reasonably be represented as a JSON object, and so the object-wrapper-with-constructor-name-key approach is arguably the most reasonable thing to do.
It's not the only thing we can do, though, since we do know statically whether the ADT contains case classes or only case objects. First we need a new type class that witnesses that an ADT is made up only of case objects (note that I'm assuming a fresh start here, but it should be possible to make this work alongside generic derivation):
import shapeless._
import shapeless.labelled.{ FieldType, field }
trait IsEnum[C <: Coproduct] {
def to(c: C): String
def from(s: String): Option[C]
}
object IsEnum {
implicit val cnilIsEnum: IsEnum[CNil] = new IsEnum[CNil] {
def to(c: CNil): String = sys.error("Impossible")
def from(s: String): Option[CNil] = None
}
implicit def cconsIsEnum[K <: Symbol, H <: Product, T <: Coproduct](implicit
witK: Witness.Aux[K],
witH: Witness.Aux[H],
gen: Generic.Aux[H, HNil],
tie: IsEnum[T]
): IsEnum[FieldType[K, H] :+: T] = new IsEnum[FieldType[K, H] :+: T] {
def to(c: FieldType[K, H] :+: T): String = c match {
case Inl(h) => witK.value.name
case Inr(t) => tie.to(t)
}
def from(s: String): Option[FieldType[K, H] :+: T] =
if (s == witK.value.name) Some(Inl(field[K](witH.value)))
else tie.from(s).map(Inr(_))
}
}
And then our generic Encoder instances:
import io.circe.Encoder
implicit def encodeEnum[A, C <: Coproduct](implicit
gen: LabelledGeneric.Aux[A, C],
rie: IsEnum[C]
): Encoder[A] = Encoder[String].contramap[A](a => rie.to(gen.to(a)))
Might as well go ahead and write the decoder too.
import cats.data.Xor, io.circe.Decoder
implicit def decodeEnum[A, C <: Coproduct](implicit
gen: LabelledGeneric.Aux[A, C],
rie: IsEnum[C]
): Decoder[A] = Decoder[String].emap { s =>
Xor.fromOption(rie.from(s).map(gen.from), "enum")
}
And then:
scala> import io.circe.jawn.decode
import io.circe.jawn.decode
scala> import io.circe.syntax._
import io.circe.syntax._
scala> (On: State).asJson.noSpaces
res0: String = "On"
scala> (Off: State).asJson.noSpaces
res1: String = "Off"
scala> decode[State](""""On"""")
res2: cats.data.Xor[io.circe.Error,State] = Right(On)
scala> decode[State](""""Off"""")
res3: cats.data.Xor[io.circe.Error,State] = Right(Off)
Which is what we wanted.

Related

How to get the name of a class as a string literal at compile time using shapeless?

This is a follow-up question of:
How to get the name of a case class field as a string/symbol at compile time using shapeless?
Assuming that I want to write a recursive converter that can convert a product type:
case class Prod (
a: Int,
b: String
)
into a Record, but unlike the above question which uses each case class fields (a, b) as keys, I want to use each class name or type/type-constructor name directly. So this product type becomes a Record at compile time:
"Int" ->> Int
"String" ->> String
(probably not a good enough use case, but you got the idea)
One key step of this is to use reflection to get the name of each class at compile time, and convert them into Singleton types or shapeless witness. I wonder if this capability is already provided somewhere? Or do I absolutely need a whitebox macro to make it happen?
You'll have to write a macro
import shapeless.ops.hlist.Mapper
import shapeless.{Generic, HList, Poly1, Typeable}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait FieldTypes[A <: Product] {
type Out <: HList
}
object FieldTypes {
type Aux[A <: Product, Out0 <: HList] = FieldTypes[A] { type Out = Out0 }
implicit def mkFieldTypes[A <: Product, L <: HList](implicit
generic: Generic.Aux[A, L],
mapper: Mapper[typeablePoly.type, L]
): Aux[A, mapper.Out] = null
object typeablePoly extends Poly1 {
implicit def cse[A](implicit typeable: Typeable[A]): Case[A] = macro cseImpl[A]
def cseImpl[A: c.WeakTypeTag](c: whitebox.Context)(typeable: c.Tree): c.Tree = {
import c.universe._
val str = c.eval(c.Expr[String](c.untypecheck(q"$typeable.describe")))
val tpA = weakTypeOf[A]
q"null.asInstanceOf[FieldTypes.typeablePoly.Case.Aux[$tpA, _root_.shapeless.labelled.FieldType[$str, $tpA]]]"
}
}
}
Testing:
import shapeless.{HNil, ::}
import shapeless.labelled.FieldType
implicitly[FieldTypes.Aux[Prod, FieldType["Int", Int] :: FieldType["String", String] :: HNil]]

How to extract types from tuple type?

I have a code
case class MyTypeTag[T] ()
def getTypeTags[TT <: Product : TypeTag] = {
val subtypesTags: List[MyTypeTag[Option[_]] = ???
sybtypesTags
}
val res = getTypeTags[(Int, String, Boolean)]
// res = MyTypeTag[Option[Int]] :: MyTypeTag[Option[String]] :: MyTypeTag[Option[Boolean]] :: Nil
so i want to call function getTypeTags passing any tuple type as type parameter and get list of MyTypeTag instances with each type inside tuple wrapped in Option
if in intelliJ i evaluate expression typeof[TT] i see property args with list of my types, but i do not know how to access from code. Or may be some other ways can be apllied.
Thanks in advance
Type parameter T in case class MyTypeTag[T]() must be known at compile time. But it seems you try to define it using runtime reflection. This can't work (unless you define the class at runtime: toolbox.define(q"case class MyTypeTag[T]()") but this would be tricky).
You can try to use compile-time reflection
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def getTypeTags[TT <: Product]: List[MyTypeTag[_ <: Option[_]]] =
macro getTypeTagsImpl[TT]
def getTypeTagsImpl[TT: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
weakTypeOf[TT].typeArgs.map(t => q"MyTypeTag[Option[$t]]()")
.foldRight[Tree](q"Nil")((t, ts) => q"$t :: $ts")
}
Usage:
getTypeTags[(Int, String, Boolean)] //List(MyTypeTag(), MyTypeTag(), MyTypeTag())
In order to verify that the code works properly let's temporarily modify MyTypeTag
import scala.reflect.runtime.universe.TypeTag
case class MyTypeTag[T]()(implicit val typeTag: TypeTag[T])
val res = getTypeTags[(Int, String, Boolean)]
res(0).typeTag // TypeTag[Option[Int]]
res(1).typeTag // TypeTag[Option[String]]
res(2).typeTag // TypeTag[Option[Boolean]]
You can also use Shapeless instead of macros
import shapeless.ops.hlist.{FillWith, Mapped, ToList}
import shapeless.{Generic, HList, Poly0}
case class MyTypeTag[T]()
def getTypeTags[TT <: Product] = new {
def apply[L <: HList, L1 <: HList]()(implicit
generic: Generic.Aux[TT, L],
mapped: Mapped.Aux[L, λ[X => MyTypeTag[Option[X]]], L1],
fillWith: FillWith[myTypeTagPoly.type, L1],
toList: ToList[L1, MyTypeTag[_ <: Option[_]]]
): List[MyTypeTag[_ <: Option[_]]] =
fillWith().toList
}
object myTypeTagPoly extends Poly0 {
implicit def cse[A]: Case0[MyTypeTag[Option[A]]] = at(MyTypeTag[Option[A]]())
}
getTypeTags[(Int, String, Boolean)]() // List(MyTypeTag(), MyTypeTag(), MyTypeTag())
If you make MyTypeTag covariant (MyTypeTag[+T]) then List[MyTypeTag[_ <: Option[_]]] can be replaced with List[MyTypeTag[Option[_]]].
You can't distinguish between an instance of MyTypeTag[Int] and an instance of MyTypeTag[String] at runtime (to check this for yourself, try
val l = List(MyTypeTag[Option[Int]](), MyTypeTag[Option[String]](), MyTypeTag[Option[Boolean]]())
and see what that gives you, and what you can and can't do with it), so the answer to the question as you ask it is
def getTypeTags[TT <: Product](implicit tt: TypeTag[TT]): List[MyTypeTag[_]] = {
tt.tpe.typeParams.map(_ => MyTypeTag[Option[_]]())
}
You can get the type parameters with tt.tpe.typeParams, but since that's a runtime value, you can't recover that as a compile-time type T for MyTypeTag[T] since it doesn't exist at compile-time yet.
Maybe you can leverage shapeless to do whatever it is you want to do, it has ways to abstract over tuples. See https://underscore.io/books/shapeless-guide/

Get Coproduct from choice

I am trying to find a way, in shapeless, to prove that a given product type extends no sealed trait, and hence does not belong to any coproduct. Given the following sealed trait hierarchy:
sealed trait Foo
case class Bar(a: Char) extends Foo
case class Baz(b: Int) extends Foo
I know I can use shapeless.ops.coproduct.Basis to prove that a given choice, or subsequence of choices, belongs to a co-product. Eg:
import shapeless._
type L = Bar :+: Baz :+: CNil
implicitly[ops.coproduct.Basis[L, Bar :+: CNil]]
What I am after now, is an operation to get the coproduct from a choice. Eg. given Bar or Baz, I would like to get back L, or alternatively, the type of the sealed base trait Foo.
Is this something that shapeless can do? alternatively, is it possible to do it with macros?
Update:
I ended up writing a fairly involved implicit macro...
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait SealedBaseTraitOf[A] {
type Repr
}
object SealedBaseTraitOf {
def materializeImpl[A](c: whitebox.Context)(implicit tag: c.WeakTypeTag[A]): c.Expr[SealedBaseTraitOf[A]] = {
import c.universe._
val a = weakTypeOf[A].typeSymbol
val baseClasses = a.asClass.baseClasses
val maybeBaseTrait =
baseClasses
.find(t => t.asClass.isTrait && t.asClass.isSealed)
.map(_.asType.name)
val repr = maybeBaseTrait.map(t => tq"$t").getOrElse(tq"Nothing")
println(s"Got a repr: $repr")
c.Expr[SealedBaseTraitOf[A]](q"""
new SealedBaseTraitOf[$a] {
type Repr = $repr
}
""")
}
implicit def materialize[A]:SealedBaseTraitOf[A] = macro materializeImpl[A]
}
Putting all together, the macro can be used as follows:
case class ExtendsNothing(a: Int)
sealed trait X
case class ExtendsX(b: Char) extends X
import shapeless._
val bt1 = the[SealedBaseTraitOf[ExtendsNothing]]
implicitly[bt1.Repr =:= Nothing]
val bt2 = the[SealedBaseTraitOf[ExtendsX]]
implicitly[bt2.Repr =:= X]
val coprodX = Generic[X]
val coprodBt2 = Generic[bt2.Repr]
implicitly[coprodX.Repr =:= coprodBt2.Repr]
implicitly[ops.coproduct.Basis[coprodBt2.Repr, ExtendsX :+: CNil]]
While getting close to a solution, I am still hoping to find something a bit less involved, possibly which doesn't involve using macros.

Converting nested case classes to nested Maps using Shapeless

I am trying to solve [this][1] question using Shapeless, in summary it's about converting a nested case class to Map[String,Any], here is the example:
case class Person(name:String, address:Address)
case class Address(street:String, zip:Int)
val p = Person("Tom", Address("Jefferson st", 10000))
The goal is to convert p to following:
Map("name" -> "Tom", "address" -> Map("street" -> "Jefferson st", "zip" -> 10000))
I am trying to do it using Shapeless LabelledGeneric, here is what I have so far:
import shapeless._
import record._, syntax.singleton._
import ops.record._
import shapeless.ops.record._
def writer[T,A<:HList,H<:HList](t:T)
(implicit lGeneric:LabelledGeneric.Aux[T,A],
kys:Keys.Aux[A,H],
vls:Values[A]) = {
val tGen = lGeneric.to(t)
val keys = Keys[lGeneric.Repr].apply
val values = Values[lGeneric.Repr].apply(tGen)
println(keys)
println(values)
}
I am trying to have a recursive writer check each value and try to make Map for each element in value. The above code works fine but when I want to iterate over values with a sample Poly, using the following code I got these errors.
values.map(identity)
//or
tGen.map(identity)
Error:(75, 19) could not find implicit value for parameter mapper: shapeless.ops.hlist.FlatMapper[shapeless.poly.identity.type,vls.Out]
values.flatMap(identity)
^
Error:(75, 19) not enough arguments for method flatMap: (implicit mapper: shapeless.ops.hlist.FlatMapper[shapeless.poly.identity.type,vls.Out])mapper.Out.
Unspecified value parameter mapper.
values.flatMap(identity)
^
I don't know why I'm getting that error. I also would be happy to know if there is an easier way to do the whole thing using Shapeless.
[1]: Scala macros for nested case classes to Map and other way around
Any time you want to perform an operation like flatMap on an HList whose type isn't statically known, you'll need to provide evidence (in the form of an implicit parameter) that the operation is actually available for that type. This is why the compiler is complaining about missing FlatMapper instances—it doesn't know how to flatMap(identity) over an arbitrary HList without them.
A cleaner way to accomplish this kind of thing would be to define a custom type class. Shapeless already provides a ToMap type class for records, and we can take it as a starting point, although it doesn't provide exactly what you're looking for (it doesn't work recursively on nested case classes).
We can write something like the following:
import shapeless._, labelled.FieldType, record._
trait ToMapRec[L <: HList] { def apply(l: L): Map[String, Any] }
Now we need to provide instances for three cases. The first case is the base case—the empty record—and it's handled by hnilToMapRec below.
The second case is the case where we know how to convert the tail of the record, and we know that the head is something that we can also recursively convert (hconsToMapRec0 here).
The final case is similar, but for heads that don't have ToMapRec instances (hconsToMapRec1). Note that we need to use a LowPriority trait to make sure that this instance is prioritized properly with respect to hconsToMapRec0—if we didn't, the two would have the same priority and we'd get errors about ambiguous instances.
trait LowPriorityToMapRec {
implicit def hconsToMapRec1[K <: Symbol, V, T <: HList](implicit
wit: Witness.Aux[K],
tmrT: ToMapRec[T]
): ToMapRec[FieldType[K, V] :: T] = new ToMapRec[FieldType[K, V] :: T] {
def apply(l: FieldType[K, V] :: T): Map[String, Any] =
tmrT(l.tail) + (wit.value.name -> l.head)
}
}
object ToMapRec extends LowPriorityToMapRec {
implicit val hnilToMapRec: ToMapRec[HNil] = new ToMapRec[HNil] {
def apply(l: HNil): Map[String, Any] = Map.empty
}
implicit def hconsToMapRec0[K <: Symbol, V, R <: HList, T <: HList](implicit
wit: Witness.Aux[K],
gen: LabelledGeneric.Aux[V, R],
tmrH: ToMapRec[R],
tmrT: ToMapRec[T]
): ToMapRec[FieldType[K, V] :: T] = new ToMapRec[FieldType[K, V] :: T] {
def apply(l: FieldType[K, V] :: T): Map[String, Any] =
tmrT(l.tail) + (wit.value.name -> tmrH(gen.to(l.head)))
}
}
Lastly we provide some syntax for convenience:
implicit class ToMapRecOps[A](val a: A) extends AnyVal {
def toMapRec[L <: HList](implicit
gen: LabelledGeneric.Aux[A, L],
tmr: ToMapRec[L]
): Map[String, Any] = tmr(gen.to(a))
}
And then we can demonstrate that it works:
scala> p.toMapRec
res0: Map[String,Any] = Map(address -> Map(zip -> 10000, street -> Jefferson st), name -> Tom)
Note that this won't work for types where the nested case classes are in a list, tuple, etc., but you could extend it to those cases pretty straightforwardly.
I have a problem with an approach provided by Travis Brown.
Some of nesting case classes are not converted to Map https://scalafiddle.io/sf/cia2jTa/0.
The answer was found here.
To correct the solution just wrap ToMapRec[T] in implicit parameters to Lazy[ToMapRec[T]]. Corrected fiddle https://scalafiddle.io/sf/cia2jTa/1

How to shapeless case classes with attributes and typeclasses?

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.
First, you need to write generic serializers for HList. That is, you need to specify how to serialize H :: T and HNil:
implicit def hconsDatatype[H, T <: HList](implicit hd: Datatype[H],
td: Datatype[T]): Datatype[H :: T] =
new Datatype[H :: T] {
override def serialize(value: H :: T): NodeSeq = value match {
case h :: t =>
val sh = hd.serialize(h)
val st = td.serialize(t)
(sh ++ st).theSeq
}
override def deserialize(from: NodeSeq): Deserialized[H :: T] =
(hd.deserialize(from.head) |#| td.deserialize(from.tail)) {
(h, t) => h :: t
}
}
implicit val hnilDatatype: Datatype[HNil] =
new Datatype[HNil] {
override def serialize(value: HNil): NodeSeq = NodeSeq()
override def deserialize(from: NodeSeq): Deserialized[HNil] =
Success(HNil)
}
Then you can define a generic serializer for any type which can be deconstructed via Generic:
implicit def genericDatatype[T, R](implicit gen: Generic.Aux[T, R],
rd: Lazy[Datatype[R]]): Datatype[T] =
new Datatype[T] {
override def serialize(value: T): NodeSeq =
rd.value.serialize(gen.to(value))
override def deserialize(from: NodeSeq): Deserialized[T] =
rd.value.deserialize(from).map(rd.from)
}
Note that I had to use Lazy because otherwise this code would break the implicit resolution process if you have nested case classes. If you get "diverging implicit expansion" errors, you could try adding Lazy to implicit parameters in hconsDatatype and hnilDatatype as well.
This works because Generic.Aux[T, R] links the arbitrary product-like type T and a HList type R. For example, for this case class
case class A(x: Int, y: String)
shapeless will generate a Generic instance of type
Generic.Aux[A, Int :: String :: HNil]
Consequently, you can delegate the serialization to recursively defined Datatypes for HList, converting the data to HList with Generic first. Deserialization works similarly but in reverse - first the serialized form is read to HList and then this HList is converted to the actual data with Generic.
It is possible that I made several mistakes in the usage of NodeSeq API above but I guess it conveys the general idea.
If you want to use LabelledGeneric, the code would become slightly more complex, and even more so if you want to handle sealed trait hierarchies which are represented with Coproducts.
I'm using shapeless to provide generic serialization mechanism in my library, picopickle. I'm not aware of any other library which does this with shapeless. You can try and find some examples how shapeless could be used in this library, but the code there is somewhat complex. There is also an example among shapeless examples, namely S-expressions.
Vladimir's answer is great and should be the accepted one, but it's also possible to do this a little more nicely with Shapeless's TypeClass machinery. Given the following setup:
import scala.xml.NodeSeq
import scalaz._, Scalaz._
trait Serializer[T] {
def serialize(value: T): NodeSeq
}
trait Deserializer[T] {
type Deserialized[T] = Validation[AnyErrors, T]
type AnyError = Throwable
type AnyErrors = NonEmptyList[AnyError]
def deserialize(from: NodeSeq): Deserialized[T]
}
trait Datatype[T] extends Serializer[T] with Deserializer[T]
We can write this:
import shapeless._
object Datatype extends ProductTypeClassCompanion[Datatype] {
object typeClass extends ProductTypeClass[Datatype] {
def emptyProduct: Datatype[HNil] = new Datatype[HNil] {
def serialize(value: HNil): NodeSeq = Nil
def deserialize(from: NodeSeq): Deserialized[HNil] = HNil.successNel
}
def product[H, T <: HList](
dh: Datatype[H],
dt: Datatype[T]
): Datatype[H :: T] = new Datatype[H :: T] {
def serialize(value: H :: T): NodeSeq =
dh.serialize(value.head) ++ dt.serialize(value.tail)
def deserialize(from: NodeSeq): Deserialized[H :: T] =
(dh.deserialize(from.head) |#| dt.deserialize(from.tail))(_ :: _)
}
def project[F, G](
instance: => Datatype[G],
to: F => G,
from: G => F
): Datatype[F] = new Datatype[F] {
def serialize(value: F): NodeSeq = instance.serialize(to(value))
def deserialize(nodes: NodeSeq): Deserialized[F] =
instance.deserialize(nodes).map(from)
}
}
}
Be sure to define these all together so they'll be properly companioned.
Then if we have a case class:
case class Foo(bar: String, baz: String)
And instances for the types of the case class members (in this case just String):
implicit object DatatypeString extends Datatype[String] {
def serialize(value: String) = <s>{value}</s>
def deserialize(from: NodeSeq) = from match {
case <s>{value}</s> => value.text.successNel
case _ => new RuntimeException("Bad string XML").failureNel
}
}
We automatically get a derived instance for Foo:
scala> case class Foo(bar: String, baz: String)
defined class Foo
scala> val fooDatatype = implicitly[Datatype[Foo]]
fooDatatype: Datatype[Foo] = Datatype$typeClass$$anon$3#2e84026b
scala> val xml = fooDatatype.serialize(Foo("AAA", "zzz"))
xml: scala.xml.NodeSeq = NodeSeq(<s>AAA</s>, <s>zzz</s>)
scala> fooDatatype.deserialize(xml)
res1: fooDatatype.Deserialized[Foo] = Success(Foo(AAA,zzz))
This works about the same as Vladimir's solution, but it lets Shapeless abstract some of the boring boilerplate of type class instance derivation so you don't have to get your hands dirty with Generic.