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

时间:2010-07-23 06:21:36

标签: scala overloading type-inference overload-resolution

我在Scala中有this class

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的成员”)。似乎转换必须是Tapper[String],这意味着参数的类型是String => Unit(或(String => Unit)*)。

有趣的是,如果我发表 tap定义,那么它就会编译。

1 个答案:

答案 0 :(得分:17)

  

6.26.3重载决议

     

首先确定一组   潜在的功能   根据形状适用   参数

     

...

     

如果只有一种选择   在B中,选择了另一种选择。

     

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

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

所以typer会像以下那样继续:

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