如果我有一个ADT和一个类型类,有没有办法让我在编译时确保每个ADT子类型都有一个类型类的实例?
仅举一个例子 - 我真的希望这不能编译,因为_drinkType
没有A
的实例
Baz
这是我自己的代码,所以如果需要,我很乐意改变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
的任何子类List
来实现这一点(我无法控制)超过那部分而不是改变Foo
我猜。在这里,我试图确保如果某人稍后更改g
,则会出现编译错误,让用户知道他们需要实现适当的Foo
答案 0 :(得分:2)
您可以使用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
,因此隐式被自动拾取。
但是,对于此特定的 A
和g
,您可能不需要类型类:
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())
答案 1 :(得分:1)
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
并解决此问题。