在Shapeless中,我可以为非案例类提供一个`LabelledGeneric`实例,它将启用类型类实例的自动派生吗?

时间:2015-01-23 22:26:58

标签: scala code-generation typeclass shapeless

我有一个json库,通过遵循Scala中的类型类模式来定义 ReadCodec的概念(定义如何读取json)。此库使用无形'Typeclass构造来免费为任何案例类派生 ReadCodec的自动实例。

现在,我有一个代码生成器,在Scala中生成的类不是案例类(但可能应该是)。我可以让代码生成器为每个类生成ReadCodec实例,这就是我们现在所拥有的。但是现在如果我希望生成的类支持一些新的序列化形式,我需要修改代码生成以输出这个新类型类的实例。

有没有办法我可以修改代码生成器以输出具有 LabelledGeneric实例的类或其他可以利用生成(通过宏)自动实例的机制的其他内容班?这样,生成的代码可以与使用Shapeless'Typeclass实现实例的任何类型类模式互操作。

1 个答案:

答案 0 :(得分:6)

有一个棘手的步骤,但您绝对可以定义自己的LabelledGeneric实例。为了举例,我将使用Alex Archambault的argonaut-shapeless库,它不使用TypeClass,但确实使用LabelledGeneric,原理是相同的。

我们班的第一个:

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

然后是我们的实例:

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
}

请注意,我们需要一种方法来引用Repr中记录键的单例类型,因此我们首先定义几个见证。这是棘手的部分。

现在你只需写下:

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

然后:

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

如果您愿意,我可以将LabelledTypeClass示例放在一起,但这个想法完全相同。