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