为仅具有一个字段的案例类派生类型类实例

问题描述:

我正在研究CSV解析库(tabulate)。它使用简单的类型进行编码/解码:例如,编码通过CellEncoder(编码单个单元)和RowEncoder(编码整行)的实例完成。为仅具有一个字段的案例类派生类型类实例

使用不成形,我发现它非常简单的自动导出以下类型的类实例:

  • RowEncoder[A]如果A是一个案例类,其字段都有CellEncoder
  • RowEncoder[A]如果A是ADT,其替代品全部具有RowEncoder
  • CellEncoder[A]如果A是ADT,其替代品全部具有CellEncoder

的事情是,这最后的一个结果是在现实生活中几乎完全无用的:一个ADT的替代品几乎都是case类,我不能为具有多个字段的情况下,类派生一个CellEncoder

但是,我希望能够做的,是派生一个CellEncoder案件类别,其中有一个单一字段的类型有CellEncoder。这将包括,例如,Either,scalaz的\/,猫Xor ...

这是我到目前为止有:

implicit def caseClass1CellEncoder[A, H](implicit gen: Generic.Aux[A, H :: HNil], c: CellEncoder[H]): CellEncoder[A] = 
    CellEncoder((a: A) => gen.to(a) match { 
     case h :: t => c.encode(h) 
    }) 

能正常工作明确适用于:

case class Bar(xs: String) 
caseClass1CellEncoder[Bar, String] 
res0: tabulate.CellEncoder[Bar] = [email protected] 

但我不能隐含起作用,下面的失败:

implicitly[CellEncoder[Bar]] 
>> could not find implicit value for parameter e: tabulate.CellEncoder[Test.this.Bar] 

我也试过以下,没有更多的成功:

implicit def testEncoder[A, H, R <: H :: HNil](implicit gen: Generic.Aux[A, R], c: CellEncoder[H]): CellEncoder[A] = 
     CellEncoder((a: A) => gen.to(a) match { 
     case h :: t => c.encode(h) 
     }) 

我缺少的东西?我试图做甚至可能吗?

这是一个有点棘手得到H正确地推断,但是你可以用<:<实例做到这一点:

import shapeless._ 

case class CellEncoder[A](encode: A => String) 

implicit val stringCellEncoder: CellEncoder[String] = CellEncoder(identity) 
implicit val intCellEncoder: CellEncoder[Int] = CellEncoder(_.toString) 

case class Bar(xs: String) 

implicit def caseClass1CellEncoder[A, R, H](implicit 
    gen: Generic.Aux[A, R], 
    ev: R <:< (H :: HNil), 
    c: CellEncoder[H] 
): CellEncoder[A] = CellEncoder(
    (a: A) => ev(gen.to(a)) match { 
    case h :: t => c.encode(h) 
    } 
) 

(我为一个完整的工作示例的目的由一个简单的CellEncoder。 )

这工作,因为R可以当编译器寻找一个Generic.Aux[A, R]情况来推断,然后可以指导H推理寻找一个ev值时。

+1

我需要长时间思考这个问题,但我可以证实它是有效的。巧合的是,我在Circe的代码中花了很多时间来理解案例类的自动类型派生,所以谢谢你的答案和Circe! –

+0

我正在努力解决这个问题 - 编写一个CellDecoder('String => A'),因为R

+0

@NicolasRinaudo一个新问题是适当的。 –