为什么foo1失败而foo2成功?编译器不应该自动检查Blah的所有超类型吗?
trait Foo[A] {
def bar: A
}
trait Bleh;
case class Blah extends Bleh;
implicit object BlehFoo extends Foo[Bleh]
def foo1[A:Foo](a:A) = a
def foo2[A,B:Foo](a:A)(implicit aToB: A => B) = aToB(a)
// Shouldn't it automatically use Bleh?
foo1(Blah())
// Failure: could not find implicit value for evidence parameter of type Foo[Blah]
foo2(Blah())
// Success: Bleh = Blah()
答案 0 :(得分:6)
由于Foo[Bleh]
不是Foo[Blah]
,您无法将Foo[Bleh]
用作Foo[Blah]
。您应该在Foo
上设置A
逆变,以Foo[Bleh]
作为Foo[Blah]
。
trait Foo[-A] {
def bar(a: A) = println(a) // to make Foo contravariant
}
这很好用:
scala> foo1(Blah())
res0: Blah = Blah()
您的原始代码包含您问题的答案。假设您可以将原始Foo[Bleh]
用作Foo[Blah]
:
def foo1[A:Foo](): A = implicitly[Foo[A]].bar
val b: Blah = foo1[Blah]()
如果Foo[Bleh]
使用Bleh
,bar
会导致Blah
,但您期望Bleh
且Blah
不是Foo[Bleh]
{1}}。
幸运的是,编译器不允许您将原始Foo[Blah]
用作scala> trait Foo[-A] {
| def bar: A
| }
<console>:8: error: contravariant type A occurs in covariant position in type => A of method bar
def bar: A
^
:
foo1[Bleh](Blah())
类型推断
这很好用:
A
但编译器不会在此处推断类型参数Bleh
为A:Foo
。为了理解“为什么”,我们应该知道def foo1[A:Foo](a:A) = a // syntax sugar
def foo1[A](a:A)(implicit ev: Foo[A]) = a // same method
的含义:
A:Foo
(a:A)
是添加隐式参数的语法糖。
如果您有2个参数组,编译器将推断第一组中的类型,然后将该类型视为已知类型。因此,在第一个参数组Blah
类型推断后,类型{{1}}已知,第二个参数组不会影响类型参数。