确保类型类的实例
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
,因此会自动提取隐式。
然而,对于这个具体A
和g
,你可能真的不需要类型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())
如果我有一个 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
,因此会自动提取隐式。
然而,对于这个具体A
和g
,你可能真的不需要类型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())