为什么Scala编译器不接受这个lambda作为参数?

Dan*_*owy 1 generics lambda scala

假设我有一个接口Thing:

abstract class Thing[A](a_thing: A) {
  def thingA = a_thing
}
Run Code Online (Sandbox Code Playgroud)

我实现Thing如下:

class SpecificThing(a: String) extends Thing[String](a)
Run Code Online (Sandbox Code Playgroud)

此外,假设我有一个函数,它接受一个Thing和一个lambda做的事情Thing作为参数:

def doSomething[A](fn: Thing[A] => A, t: Thing[A]) : A = fn(t)
Run Code Online (Sandbox Code Playgroud)

现在,让我们使用这些东西:

val st = new SpecificThing("hi")
val fn1: (Thing[String]) => String = (t: Thing[String]) => { t.thingA }
println(doSomething(fn1, st))
Run Code Online (Sandbox Code Playgroud)

这打印hi.到现在为止还挺好.但我很懒,而且我不喜欢打字这么多,所以我将程序改为以下内容:

type MyThing = Thing[String]
val st = new SpecificThing("hi")
val fn2: (MyThing) => String = (t: MyThing) => { t.thingA }
println(doSomething(fn2, st))
Run Code Online (Sandbox Code Playgroud)

这也印刷hi.极好!编译器可以告诉a SpecificThing既是a Thing[String]又是a MyThing.但是这个案子怎么样?

val st = new SpecificThing("hi")
val fn3: (SpecificThing) => String = (t: SpecificThing) => { t.thingA }
println(doSomething(fn3, st))
Run Code Online (Sandbox Code Playgroud)

现在我得到:

Error:(14, 23) type mismatch;
 found   : SpecificThing => String
 required: Thing[?] => ?
  println(doSomething(fn3, st))
                      ^
Run Code Online (Sandbox Code Playgroud)

这是怎么回事?什么是Thing[?]

lmm*_*lmm 6

f3不是Thing[String] => String,它是一个SpecificThing => String.有关他们无法兼容的原因的示例:

class SpecificThing2 extends Thing[String] {
  def thingB = 2.0
}
val f4: SpecificThing2 => String = {
  st: SpecificThing2 => f"%f${st.thingB / 3.0}"
}
val t = new Thing[String]("haha"){}
f4(t) // would be an error when f4 tried to access t.thingB
Run Code Online (Sandbox Code Playgroud)

更正式地说,它的第一个类型参数Function1逆变的Function1[-T, +R].

A Thing[?]是它的样子; 它Thing[X]适用于某种未知类型X.编译器试图推断出类型A应该是什么,但是它不能使它工作:它需要Thing[A] => A一些(未知到它)类型A,并且你传递它SpecificThing => String; 这是错误.