使用类型别名和多参数列表函数键入推断

时间:2017-04-26 13:12:42

标签: scala

编译此代码

case class MyType()
object TestMe extends App {
  type Fun[T] = T => Int
  def myFun[T](x: T): Int = ???
  def matcher[T](f: Fun[T])(p: T): Int = ???
  var f = myFun[MyType] _
  val p = MyType()
  matcher(f)(p)
}

因此错误而失败:

Error:(16, 11) type mismatch;
 found   : ... MyType => Int
 required: ... TestMe.Fun[T]
    (which expands to)  T => Int
  matcher(f)(p)

如下所示更改代码可以解决问题:

case class MyType()
object TestMe extends App {
  type Fun[T] = T => Int
  def myFun[T](x: T): Int = ???
  def matcher[T](f: Fun[T])(p: T): Int = ???
  var f: Fun[MyType] = myFun[MyType] // <-- Explicit type
  val p = MyType()
  matcher(f)(p)
}

同时更改参数顺序可以解决问题:

case class MyType()
object TestMe extends App {
  type Fun[T] = T => Int
  def myFun[T](x: T): Int = ???
  def matcher[T](p: T)(f: Fun[T]): Int = ??? // <-- Flipping the argument, so the first argument have explicitly the parametric type
  var f = myFun[MyType] _
  val p = MyType()
  matcher(p)(f)  // <-- Calls with flipped arguments
}

我的理解(我想由于我缺乏Scala知识)是'type'只是创建类型别名,但看起来并不像那样。 有人能解释编译失败的原因吗?

由于

1 个答案:

答案 0 :(得分:3)

这是类型推断的限制以及typer在编译时如何解析类型。

在Scala中,类型可以在参数列表之间进行(而不是在它们内部)。当您首次将p类型T作为参数放在第一个参数列表中时,typer可以先将T绑定到MyType,然后知道第二个参数列表f属于Fun[MyType],因为它可以推断T

|-- matcher(p)(f) : pt=Unit EXPRmode (site: method main in Test)
|    |    |    |    |-- matcher(p) BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Test)
|    |    |    |    |    |-- matcher BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Test)
|    |    |    |    |    |    [adapt] [T](p: T)(f: Fun[T])Int adapted to [T](p: T)(f: Fun[T])Int
|    |    |    |    |    |    \-> (p: T)(f: Fun[T])Int
|    |    |    |    |    |-- p BYVALmode-EXPRmode-POLYmode (silent: method main in Test)
|    |    |    |    |    |    \-> MyType
|    |    |    |    |    solving for (T: ?T)
|    |    |    |    |    \-> (f: Fun[MyType])Int
|    |    |    |    |-- f : pt=Fun[MyType] BYVALmode-EXPRmode (site: method main in Test)
|    |    |    |    |    \-> MyType => Int

反过来说不起作用,编译器无法从T类型的函数推断MyTypeMyType => Int(请记住,函数在参数中也可能是逆变的型):

-- matcher(f)(p) : pt=Unit EXPRmode (site: method main in Test)
|    |    |    |    |-- matcher(f) BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Test)
|    |    |    |    |    |-- matcher BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Test)
|    |    |    |    |    |    [adapt] [T](f: Fun[T])(p: T)Int adapted to [T](f: Fun[T])(p: T)Int
|    |    |    |    |    |    \-> (f: Fun[T])(p: T)Int
|    |    |    |    |    |-- f : pt=Fun[?] BYVALmode-EXPRmode-POLYmode (silent: method main in Test)
|    |    |    |    |    |    \-> MyType => Int
|    |    |    |    |    solving for (T: ?T)
|    |    |    |    |    [search #1] start `MyType => Int`, searching for adaptation to pt=(MyType => Int) => Fun[T] (silent: method main in Test) implicits disabled
|    |    |    |    |    [search #2] start `MyType => Int`, searching for adaptation to pt=(=> MyType => Int) => Fun[T] (silent: method main in Test) implicits disabled

如您所知,切换参数列表有效。您还可以通过在matcher方法上声明类型来明确帮助编译器推断类型:

matcher[MyType](f)(p)