确保类型类的实例

Ensure instance of typeclass

如果我有一个 ADT 和一个类型 class,有没有办法让我在编译时确保 ADT 的每个子类型都有一个类型 class 的实例?

举个例子——我真的很希望它不编译,因为 Baz

没有 A 的实例
sealed trait Foo
final case class Bar(s: String) extends Foo
final case class Baz(i: Int) extends Foo

trait A[T <: Foo] {
  type O
  def f(t: T): O
}

implicit val barA = new A[Bar] {
  type O = String
  def f(t: Bar): O = t.s
}

这都是我自己的代码,所以我很乐意根据需要更改 Foo 的编码(也许无形的副产品可以帮助我解决这个问题?)

编辑

抱歉,应该提到 - 我有一个有点像这样的功能我想实现(假设我的实例在我导入的对象中并且它们是范围内的唯一实现)

def g[T <: Foo](fs: List[T])(implicit a: A[T]): List[a.O] = fs.map(a.f(_))

从下面的评论来看,我似乎也应该说调用 g 的东西可以用 [=14] 的任何子 class 的 List 来实现=](我想除了改变 g 之外,我无法控制那部分)。在这里,我试图确保如果有人稍后更改 Foo,则会出现编译器错误,让用户知道他们需要实施适当的 A

trait Foo[T] {
  this: ImplementThis[T] =>
}

case class Bar() extends Foo[String] with ImplementThis[String] {
  override def f(t: String): String = {
    t
  }
}
case class Baz() extends Foo[Int] with ImplementThis[Int] {
  override def f(t: Int): Int = {
    t
  }
}


trait ImplementThis[T] {
  type O
  def f(t: T): O
}

尝试这样的事情。这将对定义的 Foo 的任何子类强制实施 def f(t: T):O

def g[T <: Foo](fs: List[T])(implicit a: A[T]): List[a.O] = fs.map(a.f(_))

据此,我假设您希望 Foo 的所有子 类 都有一个 def f,这样它们就不会在运行时失败。我认为我的上述建议将强制执行 def f 实施并解决此问题。

您可以使用 F-bounded 多态性(又名 Curiously Recurrent Template Pattern):

sealed abstract class Foo[Self <: Foo](implicit val hasA: A[Self])
final case class Bar(s: String) extends Foo[Bar]
final case class Baz(i: Int) extends Foo[Baz]
使用

abstract class 而不是 trait,因此会自动提取隐式。

然而,对于这个具体Ag,你可能真的不需要类型class:

sealed trait Foo[O] {
  def f(): O
}

final case class Bar(s: String) extends Foo[String] {
  def f() = s
}

def g(fs: List[Foo[O]]): List[O] = fs.map(_.f())