简体   繁体   中英

In Shapeless, can I supply an instance of `LabelledGeneric` for non case classes that will enable automatic derivation of typeclass instances?

I have a json library that defines a notion of ReadCodec (which defines how to read json) by following the typeclass pattern in Scala. This library uses Shapeless ' Typeclass construct to derive automatic instances of ReadCodec for any case class for free.

Now, I have a code generator that generates classes in Scala that are not case classes (but probably should be). I could have the code generator generate instances of ReadCodec for each class which is what we have right now. But now if I want the generated classes to support some new form of serialization, I need to modify code generation to output instances for this new typeclass.

Is there a way I can modify the code generator to output classes with instances of LabelledGeneric or something else that would tap into the mechanism that generates (via macro) automatic instances for case classes? This way, the generated code can interop with any typeclass pattern that uses Shapeless' Typeclass for materialization of instances.

There's one tricky step, but you definitely can define your own LabelledGeneric instances. For the sake of example here I'll use Alex Archambault's argonaut-shapeless library, which doesn't literally use TypeClass , but it does use LabelledGeneric , and the principle is the same.

First for our class:

class Foo(val i: Int, val s: String)

Then for our instance:

import shapeless._, labelled._, record._, syntax.singleton._

implicit object fooGeneric extends LabelledGeneric[Foo] {
  val iw = Witness('i)
  val sw = Witness('s)

  type Repr = FieldType[iw.T, Int] :: FieldType[sw.T, String] :: HNil

  def from(r: Repr): Foo = new Foo(r('i), r('s))
  def to(t: Foo): Repr = ('i ->> t.i) :: ('s ->> t.s) :: HNil
}

Note that we need a way to refer to the singleton types for the record keys in Repr , so we define a couple of witnesses first. That's the tricky part.

Now you just write this:

import argonaut._, Argonaut._, Shapeless._

And then:

scala> implicitly[EncodeJson[Foo]].encode(new Foo(42, "foo"))
res0: argonaut.Json = {"s":"foo","i":42}

I could put together a LabelledTypeClass example if you'd like, but the idea is exactly the same.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM