假设我有一个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
)?
答案 0 :(得分:1)
隐含的这个afoo
不仅在其他三个存在的情况下无用,甚至很糟糕,因为它会因MatchError
值new A {}
而失败。
如果您的隐含涵盖Foo[A]
类型的所有可能(有效)值且A
没有&#39,我就不明白为什么您需要afoo
这样的实例; t添加任何东西。
我可以想象,如果你有一个功能
def foo(a: A)(implicit f: Foo[A]): String = f.foo(a)
然后,当然,a1foo
,a2foo
或a3foo
都不适合。 afoo
会foo(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
(与A3
和foo(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写出明确的暗示类型。