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

Posted

技术标签:

【中文标题】为啥 Scala 类型推断在这里失败?【英文标题】:Why does Scala type inference fail here?为什么 Scala 类型推断在这里失败? 【发布时间】:2011-03-19 22:45:43 【问题描述】:

我在 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】:

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)

【讨论】:

好主意,谢谢!我想我必须以不同的方式命名它们。 你很快就会成为新的丹尼尔,杰森! @oxbow 更好的是,他经常引用规范,这是一件好事。 如果规范有 SO 帐户就好了。他知道所有的答案!

以上是关于为啥 Scala 类型推断在这里失败?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 TypeScript 中的方法链接会导致泛型类型推断失败?

显示推断的 Scala 表达式类型

如何知道 Spark 使用 Scala 推断出的 RDD 类型是啥

Scala 编译器无法在 Spark lambda 函数中推断类型

[原创]Scala学习:关于变量(val,var,类型推断)

Scala的类型推断