派生类型 class 实例 class 仅包含一个字段
Deriving type class instances for case classes with exactly one field
我正在开发 CSV 解析库 (tabulate)。它使用简单类型 classes 进行编码/解码:例如,编码是使用 CellEncoder
(对单个单元格进行编码)和 RowEncoder
(对整行进行编码)的实例完成的。
使用 shapeless,我发现自动派生以下类型 class 实例非常简单:
RowEncoder[A]
如果 A
是 class 的情况,其字段都具有 CellEncoder
.
RowEncoder[A]
如果 A
是一个 ADT,其备选方案都具有 RowEncoder
.
CellEncoder[A]
如果 A
是一个 ADT,其备选方案都具有 CellEncoder
。
事实是,最后一个结果在现实生活中几乎完全没用:ADT 的替代方案几乎总是 classes,而我无法推导出 CellEncoder
具有多个字段的案例 class。
但是,我希望能够为具有单个字段且类型为 CellEncoder
的案例 class 派生一个 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@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)
})
我错过了什么吗?我正在尝试做的事情是否可行?
正确推断 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
。)
这是有效的,因为当编译器寻找 Generic.Aux[A, R]
实例时可以推断出 R
,然后在寻找 [=] 的值时可以指导 H
的推断17=].
我正在开发 CSV 解析库 (tabulate)。它使用简单类型 classes 进行编码/解码:例如,编码是使用 CellEncoder
(对单个单元格进行编码)和 RowEncoder
(对整行进行编码)的实例完成的。
使用 shapeless,我发现自动派生以下类型 class 实例非常简单:
RowEncoder[A]
如果A
是 class 的情况,其字段都具有CellEncoder
.RowEncoder[A]
如果A
是一个 ADT,其备选方案都具有RowEncoder
.CellEncoder[A]
如果A
是一个 ADT,其备选方案都具有CellEncoder
。
事实是,最后一个结果在现实生活中几乎完全没用:ADT 的替代方案几乎总是 classes,而我无法推导出 CellEncoder
具有多个字段的案例 class。
但是,我希望能够为具有单个字段且类型为 CellEncoder
的案例 class 派生一个 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@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)
})
我错过了什么吗?我正在尝试做的事情是否可行?
正确推断 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
。)
这是有效的,因为当编译器寻找 Generic.Aux[A, R]
实例时可以推断出 R
,然后在寻找 [=] 的值时可以指导 H
的推断17=].