【问题标题】:When does Scala need parameter types for anonymous and expanded functions?Scala 何时需要匿名函数和扩展函数的参数类型?
【发布时间】:2011-02-04 14:50:02
【问题描述】:

Scala编译器什么时候真正需要匿名函数的参数类型信息?

例如,给定这个函数:

def callOn[T,R](target: T, f: (T => R)) = f(target)

那么我不能这样使用它:

callOn(4, _.toString)
  => error: missing parameter type for expanded function ((x$1) => x$1.toString)

我必须指定

callOn(4, (_: Int).toString)

这是相当丑陋的。为什么我的示例不起作用,而集合类上的 map、filter、foldLeft 等方法似乎不需要这种显式类型?

【问题讨论】:

    标签: scala anonymous-function type-inference


    【解决方案1】:

    类型推断的技巧是将其视为一个迭代细化的过程。每个参数块都可以用来推断一些类型参数,然后可以在后续块中使用。所以采用以下定义:

    def chain[T,A,B](x: T)(fn1: T=>A)(fn2: A=>B) = fn2(fn1(x))
    

    称为:

    chain(2)(_*10)("xxx"+_)
    

    那么这是如何推断的呢?首先,我们从已知类型为Int 的块(2) 开始。将其代入T 参数,我们得到:

    def chain[A,B](x: Int)(fn1: Int=>A)(fn2: A=>B) = fn2(fn1(x))
    

    下一个参数块是(_*10),我们现在知道占位符_ 的类型是Int... 并将Int 乘以Int 得到另一个Int。即使发生溢出,返回的类型也是如此;在极端情况下它可能会抛出异常,但异常的类型为Nothing,它是类型系统中其他所有内容的子类,所以我们仍然可以说Nothing is-an Int 和@ 的推断类型987654336@仍然有效。

    推断A,方法变为:

    def chain[B](x: Int)(fn1: Int=>Int)(fn2: Int=>B) = fn2(fn1(x))
    

    只留下B,可以从("xxx"+_)推断出来。因为String + IntString,所以现在的方法是:

    def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String) = fn2(fn1(x))
    

    由于方法的返回类型直接来自fn2,为了完整性,也可以显式显示:

    def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String): String = fn2(fn1(x))
    

    你有它,所有类型都安全解析,并且方法被证明是静态有效的。


    在您的情况下,您需要先推断出T 类型,然后才能从T=>R 类型推断出R。为此,您必须将参数分成两个不同的块,以柯里化形式编写方法:

    def callOn[T,R](target: T)(f: (T => R)) = f(target)
    

    【讨论】:

      【解决方案2】:

      这个问题也在这里得到了回答:

      Passing functions for all applicable types around

      您希望,... Scala 的编译器会两次考虑这两个参数来推断正确的类型。然而,Scala 并没有这样做——它只使用从一个参数列表到下一个参数列表的信息,而不是从一个参数到下一个参数列表。这意味着参数 f 和 a [WS: target and f in this case] 是独立分析的,没有知道另一个是什么的优势。

      请注意,它确实适用于 curried 版本:

      scala> def callOn[T,R](target: T)(f: (T => R)) = f(target)
      callOn: [T,R](target: T)(f: (T) => R)R
      
      scala> callOn(4)(_.toString)
      res0: java.lang.String = 4
      
      scala> 
      

      【讨论】:

      • 感谢您的回答。假设我有这个更简单的案例:def callOn4[R](f: (Int => R)) = f(4)。为什么这里可以省略参数类型,但定义为def callOn4(f: (Int => _)) = f(4)就不行?
      • @jpp - 试着弄清楚你的第二个版本会推断出什么返回类型......
      猜你喜欢
      • 2016-11-28
      • 2017-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多