ADT的类型类实例的通用派生

时间:2016-07-03 08:18:36

标签: scala typeclass shapeless

假设我有一个ADT并输入类Foo,如下所示:

sealed trait A
case class A1() extends A
case class A2() extends A
case class A3() extends A

trait Foo[X] { def foo(x: X): String; }
object Foo {
  implicit val a1foo = new Foo[A1] { def foo(a1: A1) = "A1" }
  implicit val a2foo = new Foo[A2] { def foo(a2: A2) = "A2" }
  implicit val a3foo = new Foo[A3] { def foo(a3: A3) = "A3" }
}  

现在我可以这样写Foo[A]

implicit val afoo = new Foo[A] {
  def foo(a: A) = a match {
    case a1 : A1 => a1foo.foo(a1)
    case a2 : A2 => a2foo.foo(a2)
    case a3 : A3 => a3foo.foo(a3)
  }
}

不幸的是,此代码太 boilerplaty 。是否有可能自动删除所有样板文件并派生 Foo[A](可能使用shapeless)?

1 个答案:

答案 0 :(得分:1)

隐含的这个afoo不仅在其他三个存在的情况下无用,甚至很糟糕,因为它会因MatchErrornew A {}而失败。

如果您的隐含涵盖Foo[A]类型的所有可能(有效)值且A没有&#39,我就不明白为什么您需要afoo这样的实例; t添加任何东西。

我可以想象,如果你有一个功能

def foo(a: A)(implicit f: Foo[A]): String = f.foo(a)

然后,当然,a1fooa2fooa3foo都不适合。 afoofoo(A1()),所以foo(new A {})会编译并正常工作,但MatchError也会编译,并失败foo(new A {})。顺便说一句,如果代码中存在调用foo,它将编译时会显示有关非详尽匹配的警告,但如果不是,则会进行静默编译。

因此解决方法是修改def foo[X <: A](a: X)(implicit f: Foo[X]): String = f.foo(a) ,以采用更精确的类型:

foo(A1())

现在a1foo将编译并选择A2(与A3foo(new A {})相同),而Foo[A]只是不会编译(因为它不该&#39;。T)

更新

如果您仍希望将object Foo extends Foo_1 { implicit val a1foo: Foo[A1] = ... implicit val a2foo: Foo[A2] = ... implicit val a3foo: Foo[A3] = ... } trait Foo_1 { // this implicit has a lower priority: implicit val afoo: Foo[A] = new Foo[A] { def foo(a: A) = "A" } } 的实例作为默认案例,则可以像这样更改代码:

foo(new A {})

现在foo(A2(): A)"A"将进行编译并返回vector<Handle>::iterator

顺便说一下,

P.S。recommended写出明确的暗示类型。