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

jed*_*sah 3 code-generation scala typeclass shapeless

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

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

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

Tra*_*own 6

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

我们班的第一个:

class Foo(val i: Int, val s: String)
Run Code Online (Sandbox Code Playgroud)

那么对于我们的实例:

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
}
Run Code Online (Sandbox Code Playgroud)

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

现在你只需写下:

import argonaut._, Argonaut._, Shapeless._
Run Code Online (Sandbox Code Playgroud)

然后:

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

LabelledTypeClass如果你愿意,我可以把一个例子放在一起,但这个想法是完全一样的.