什么时候Scala需要匿名和扩展函数的参数类型?

时间:2011-02-04 14:50:02

标签: scala anonymous-function type-inference

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等方法似乎不需要这种显式类型?

2 个答案:

答案 0 :(得分:14)

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

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

称为:

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

那么这是如何推断出来的?首先,我们从已知具有类型(2)的块Int开始。将其替换回我们得到的T参数:

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

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

通过推断Int,该方法变为:

A

仅保留def chain[B](x: Int)(fn1: Int=>Int)(fn2: Int=>B) = fn2(fn1(x)) ,可以从B推断出来。由于("xxx"+_)String + Int,现在的方法是:

String

由于方法的返回类型直接来自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推断R。为此,您必须将参数拆分为两个不同的块,以咖喱形式编写方法:

T=>R

答案 1 :(得分:6)

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

Passing functions for all applicable types around

  

您希望,......对于Scala的编译器,要将两个参数都考虑到两次,以推断出正确的类型。但是,Scala并没有这样做 - 它只使用从一个参数列表到下一个参数列表的信息,而不是从一个参数到下一个参数。这意味着参数f和a [WS:在这种情况下为目标和f]是独立分析的,而不具备知道对方是什么的优势。

请注意 使用咖喱版:

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>