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

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

为什么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 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
,等等都是行业中完美的标准(并且深受喜爱)。