使用类型类隐式转换为超类型

时间:2013-08-09 13:43:31

标签: scala typeclass implicit-conversion implicit-cast

为什么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()

1 个答案:

答案 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]使用Blehbar会导致Blah,但您期望BlehBlah不是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

但编译器不会在此处推断类型参数BlehA: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}}已知,第二个参数组不会影响类型参数。