Scala 使用类型类隐式转换为超类型
为什么foo1失败而foo2成功?编译器不应该自动检查所有的超类型吗Scala 使用类型类隐式转换为超类型,scala,typeclass,implicit-conversion,Scala,Typeclass,Implicit Conversion,为什么foo1失败而foo2成功?编译器不应该自动检查所有的超类型吗 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
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()
您不能将
Foo[Bleh]
用作Foo[Blah]
,因为Foo[Bleh]
不是Foo[Blah]
。您应该在A
上设置Foo
逆变,以使用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]()
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
^
如果这里使用了Foo[Bleh]
,您将得到Bleh
,这是bar
的结果,但是您期望Blah
,并且Bleh
不是Blah
幸运的是,编译器不允许您使用原始的Foo[Bleh]
作为Foo[Blah]
:
def foo1[A:Foo](): A = implicitly[Foo[A]].bar
val b: Blah = foo1[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
^
但编译器不会在这里将类型参数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)
类型Blah
进行类型推断之后,第二个参数组不会影响类型参数。您不能将Foo[Bleh]
用作Foo[Blah]
,因为Foo[Bleh]
不是Foo[Blah]
。您应该在A
上设置Foo
逆变,以使用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]()
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
^
如果这里使用了Foo[Bleh]
,您将得到Bleh
,这是bar
的结果,但是您期望Blah
,并且Bleh
不是Blah
幸运的是,编译器不允许您使用原始的Foo[Bleh]
作为Foo[Blah]
:
def foo1[A:Foo](): A = implicitly[Foo[A]].bar
val b: Blah = foo1[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
^
但编译器不会在这里将类型参数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)进行类型推断之后,
类型Blah
是已知的,第二个参数组不会影响类型参数。我完全同意Foo[Bleh]与Foo[Blah]是不同的。然而,Blah实例从定义上来说也是一个Bleh:我真正想问的问题是,为什么在没有显式Foo[Blah]的情况下,编译器没有将Blah用作Bleh并正确解析类型类?@Refefer:我已经更新了我的答案。如果你想知道为什么类型推断可以一个接一个地使用参数组,我可以给你两个理由:1)lambda作为最后一个参数很有用;2) 更快。我完全同意Foo[Bleh]和Foo[Blah]是不同的。然而,Blah实例从定义上来说也是一个Bleh:我真正想问的问题是,为什么在没有显式Foo[Blah]的情况下,编译器没有将Blah用作Bleh并正确解析类型类?@Refefer:我已经更新了我的答案。如果你想知道为什么类型推断可以一个接一个地使用参数组,我可以给你两个理由:1)lambda作为最后一个参数很有用;2) 速度更快。问得好,但你选择的变量名让我想吐。难道它们不能更具描述性,比如超类/子类吗?Foo
,Bar
,等等在行业中都是非常标准的(也很受欢迎)。问得好,但是你选择的变量名让我想吐。难道它们不能更具描述性,比如超类/子类吗?Foo
,Bar
,等等都是行业中完美的标准(并且深受喜爱)。