【问题标题】:Why does Scala type inferencing fail here为什么 Scala 类型推断在这里失败
【发布时间】:2017-06-22 16:06:05
【问题描述】:

为什么右结合运算符在泛型类型的函数中存在问题,即使等效的左结合运算符工作得很好。

implicit class FunctionWrapper[T1, T2](func: T1 => T2) {
  def >>>[T3](funcAfter: T2 => T3): T1 => T3 = {
    func andThen funcAfter
  }

  def >>:[T0](funcBefore: T0 => T1): T0 => T2 = {
    funcBefore andThen func
  }
}

以下是要链接的函数:

def intToFloat = Int.int2float _
def floatToString = (_: Float).toString
def identityF[T] = identity(_: T)

正如预期的那样,这两个运算符都适用于具体类型的函数:

scala> (intToFloat >>> floatToString)(11)
res5: String = 11.0

scala> (intToFloat >>: floatToString)(11)
res6: String = 11.0

但是,由于某种原因,>>: 使用泛型函数失败:

scala> (intToFloat >>> identityF >>> floatToString)(11)
res7: String = 11.0

scala> (intToFloat >>: identityF >>: floatToString)(11)
<console>:16: error: type mismatch;
 found   : Nothing => Nothing
 required: T0 => Float
       (intToFloat >>: identityF >>: floatToString)(11)
                                 ^

有一些解决方法,一个是

(intToFloat >>: (identityF (_: Float)) >>: floatToString)(11)

但是在给定的上下文中推断 identityF 的类型似乎很容易,为什么它会失败?

【问题讨论】:

    标签: scala generics functional-programming type-inference


    【解决方案1】:

    当你有一个右结合运算符时,记住它是真的

    floatToString.>>:(identityF).>>:(intToFloat)
    

    由于没有为identityF 提供类型参数,它受local type inference 规则的约束。在这种情况下,它会首先尝试找到identityF 的类型参数(我认为,但反过来也会遇到麻烦);因为&gt;&gt;:的类型参数还是未知的,所以不知道函数的参数类型。它通过推断Nothing 放弃,然后找不到适合&gt;&gt;: 的类型参数。

    使用andThen&gt;&gt;&gt;,Scala 已经知道identityF 的预期参数类型,因此可以推断出正确的类型参数。

    也就是说,问题不在于&gt;&gt;: 是右关联的,而是参数和返回类型之间的不对称。如果你定义&gt;&gt;:[T0](f: T1 =&gt; T0),它会正常工作。

    【讨论】:

    • 感谢您的解释。从这个和相关问题看来,Scala 的类型推断机制暂时还不够强大。在这里,它似乎遵循了一种“贪婪”的方法并且过早放弃了,因为显然可以从 intToFloat 和 floatToString 的类型推断出 identityF 的类型。
    • 是的,它甚至从未考虑过intToFloat。另一方面,子类型化使类型推断复杂化了很多。让它更强大可能会使其更难以预测和难以指定。
    猜你喜欢
    • 2011-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多