11

我在 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)
}

现在,

"aaa".tap(_.trim)

不编译,报错

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

为什么类型不是推断为String?从错误看来,隐式转换确实会触发(否则错误将类似于“tap不是类的成员String”)。似乎转换必须是 to Tapper[String],这意味着参数的类型是String => Unit(或(String => Unit)*)。

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

4

1 回答 1

17

6.26.3 重载分辨率

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

...

如果 B 中恰好有一个备选方案,则选择该备选方案。

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

的两个重载tap都可能适用(基于参数的“形状”,它解释了 arity 和类型构造函数 FunctionN)。

所以打字机继续进行:

val x = _.trim

并失败。

更智能的算法可以采用每个备选方案的相应参数类型的最小上限,并将其用作预期类型。但这种复杂性并不值得,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)
}
于 2010-07-23T07:21:58.063 回答