我正在使用CSV解析库(tabulate)。它使用简单类型类进行编码/解码:例如,编码使用CellEncoder
(编码单个单元格)和RowEncoder
(编码整行)的实例完成。
使用无形,我发现自动派生以下类型类实例非常简单:
RowEncoder[A]
如果A
是一个案例类,其字段都有CellEncoder
。RowEncoder[A]
如果A
是ADT,其替代品都有RowEncoder
。CellEncoder[A]
如果A
是ADT,其替代品都有CellEncoder
。问题是,最后一个在现实生活中几乎完全无用:ADT的替代方案几乎总是案例类,我无法为案例类派生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] = tabulate.CellEncoder$$anon$2@7941904b
但是我不能隐含地工作,以下失败:
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)
})
我错过了什么吗?是我试图做的甚至可能吗?
答案 0 :(得分:3)
正确推断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
的推断}。