为什么Scala类型推断在这里失败?

Ale*_*nov 11 scala overloading type-inference overload-resolution

我在Scala中有这个类:

object Util {
  class Tapper[A](tapMe: A) {
    def tap(f: A => Unit): A = {
      f(tapMe)
      tapMe
    }

    def tap(fs: (A => Unit)*): A = {
      fs.foreach(_(tapMe))
      tapMe
    }
  }

  implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)
}
Run Code Online (Sandbox Code Playgroud)

现在,

"aaa".tap(_.trim)
Run Code Online (Sandbox Code Playgroud)

不编译,给出错误

错误:扩展函数缺少参数类型((x $ 1)=> x $ 1.trim)

为什么不推断类型String?从错误看来,隐式转换似乎触发了(否则错误将沿着" tap不是类的成员String"的行).似乎转换必须是Tapper[String],这意味着参数的类型是String => Unit(或(String => Unit)*).

有趣的是,如果我注释掉任何一个tap定义,那么它就会编译.

ret*_*nym 17

6.26.3重载分辨率

首先根据参数的形状确定可能适用的函数集

...

如果B中恰好有一个替代方案,则选择该替代方案.

否则,让S1 ,. ..,Sm是通过键入具有未定义的期望类型的每个参数而获得的类型的向量.

两个重载tap都可能适用(基于参数的'形状',它考虑了arity和类型构造函数FunctionN).

因此,typer继续如下:

val x = _.trim
Run Code Online (Sandbox Code Playgroud)

并失败.

更智能的算法可以采用每个备选方案的相应参数类型的最小上限,并将其用作期望类型.但这种复杂性并不值得,IMO.重载有很多极端情况,这只是另一种情况.

但是在这种情况下你可以使用一个技巧,如果你真的需要一个接受单个参数的重载:

object Util {
  class Tapper[A](tapMe: A) {
    def tap(f: A => Unit): A = {
      f(tapMe)
      tapMe
    }

    def tap(f0: A => Unit, f1: A => Unit, fs: (A => Unit)*): A = {
      (Seq(f0, f1) ++ fs).foreach(_(tapMe))
      tapMe
    }
  }

  implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)

  "".tap(_.toString)
  "".tap(_.toString, _.toString)
  "".tap(_.toString, _.toString, _.toString)
}
Run Code Online (Sandbox Code Playgroud)

  • 你很快就会成为新的丹尼尔,杰森! (3认同)
  • @oxbow甚至更好,他经常引用规范,这是一件好事. (2认同)
  • 如果只有规格有SO帐户.他知道所有的答案! (2认同)