scala - Shapeless 中 TypeClass 特征的 emptyCoproduct 和 coproduct 方法的目的是什么

标签 scala typeclass shapeless type-level-computation

我不完全清楚 emptyCoProduct 的目的是什么和 coproduct TypeClass 的方法无形中的特征。

何时使用 TypeClass trait 而不是 ProductTypeClass ?

这两种方法的实现方式有哪些示例?

最佳答案

假设我有一个简单的类型类:

trait Weight[A] { def apply(a: A): Int }

object Weight {
  def apply[A](f: A => Int) = new Weight[A] { def apply(a: A) = f(a) }
}

还有一些情况:
implicit val stringWeight: Weight[String] = Weight(_.size)
implicit def intWeight: Weight[Int] = Weight(identity)

还有一个案例类:
case class Foo(i: Int, s: String)

还有一个 ADT:
sealed trait Root
case class Bar(i: Int) extends Root
case class Baz(s: String) extends Root

我可以定义一个 ProductTypeClass我的类型类的实例:
import shapeless._

implicit object WeightTypeClass extends ProductTypeClass[Weight] {
  def emptyProduct: Weight[HNil] = Weight(_ => 0)
  def product[H, T <: HList](hw: Weight[H], tw: Weight[T]): Weight[H :: T] =
    Weight { case (h :: t) => hw(h) + tw(t) }
  def project[F, G](w: => Weight[G], to: F => G, from: G => F): Weight[F] =
    Weight(f => w(to(f)))
}

并像这样使用它:
scala> object WeightHelper extends ProductTypeClassCompanion[Weight]
defined object WeightHelper

scala> import WeightHelper.auto._
import WeightHelper.auto._

scala> implicitly[Weight[Foo]]
res0: Weight[Foo] = Weight$$anon$1@4daf1b4d

scala> implicitly[Weight[Bar]]
res1: Weight[Bar] = Weight$$anon$1@1cb152bb

scala> implicitly[Weight[Baz]]
res2: Weight[Baz] = Weight$$anon$1@74930887

但!
scala> implicitly[Weight[Root]]
<console>:21: error: could not find implicit value for parameter e: Weight[Root]
              implicitly[Weight[Root]]
                        ^

这是一个问题——它使我们的自动化类型类实例派生对 ADT 几乎毫无用处。幸运的是我们可以使用 TypeClass反而:
implicit object WeightTypeClass extends TypeClass[Weight] {
  def emptyProduct: Weight[HNil] = Weight(_ => 0)
  def product[H, T <: HList](hw: Weight[H], tw: Weight[T]): Weight[H :: T] =
    Weight { case (h :: t) => hw(h) + tw(t) }
  def project[F, G](w: => Weight[G], to: F => G, from: G => F): Weight[F] =
    Weight(f => w(to(f)))
  def emptyCoproduct: Weight[CNil] = Weight(_ => 0)
  def coproduct[L, R <: Coproduct]
    (lw: => Weight[L], rw: => Weight[R]): Weight[L :+: R] = Weight {
      case Inl(h) => lw(h)
      case Inr(t) => rw(t)
    }
}

进而:
scala> object WeightHelper extends TypeClassCompanion[Weight]
defined object WeightHelper

scala> import WeightHelper.auto._
import WeightHelper.auto._

scala> implicitly[Weight[Root]]
res0: Weight[Root] = Weight$$anon$1@7bc44e19

上面的所有其他东西仍然有效。

总结:Shapeless的Coproduct是对 ADT 的一种抽象,通常你应该提供 TypeClass 的实例。用于您的类型类,而不仅仅是 ProductTypeClass只要有可能。

关于scala - Shapeless 中 TypeClass 特征的 emptyCoproduct 和 coproduct 方法的目的是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25517069/

相关文章:

scala - 如何镜像二叉树?

scala - 无法递归地构建无形的 HList

haskell - 如何指定类型类的具体实现?

haskell - 在 haskell 中实现 Read 类型类的实例

scala - 如何将两个元组与兼容的类型结合起来?

scala - 无法在列表中获取通用对象的类型

Scala 支持向量机库

haskell - 用可变变量表示数据类型

scala - 自动将案例类转换为无形的可扩展记录?

scala - 在 Apache Spark 1.3 中向数据框添加一列