确保类型类的实例

时间:2017-04-20 12:04:52

标签: scala typeclass implicit shapeless

如果我有一个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

2 个答案:

答案 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,因此隐式被自动拾取。

但是,对于此特定的 Ag,您可能不需要类型类:

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并解决此问题。