想象一下这样的代码:
class Foo {
println("in Foo")
def foo(a: Int) = a + 1
}
现在,如果我们调用:
new Foo().foo _
Foo类的实例将生成,如预期:
in Foo
res0: (Int) => Int = <function1>
但是,如果我们调用这个:
new Foo().foo(_)
Foo的构造函数将不会被调用:
res1: (Int) => Int = <function1>
然后如果我们说:
res1(7)
那就是当富被实例化:
in Foo
res2: Int = 8
为什么埃塔扩张与局部的功能应用使类的实例有区别吗?
男孩,这是一个微妙的,但据我可以告诉它下面的斯卡拉符合规范彻底。 我会从规范的2.9版本的报价。
为了您的第一个例子:当你正确地说,你是通过方法值(§6.7)的一个特例看到ETA扩展:
The expression e _ is well-formed if e is of method type or if e is a call-by-name parameter. If e is a method with parameters, e _ represents e converted to a function type by eta expansion.
为ETA扩展算法§6.26.5中给出,你可以按照给出如下更换为表达new Foo().x1 _
{
val x1 = new Foo();
(y1: Int) => x1.(y1);
}
这意味着,当正在使用ETA膨胀,所有的子表达式在其中转换发生点评价(如果我理解短语“最大子表达”正确的意思)和最终的表达是创建一个匿名函数。
在第二个例子中,这些额外的括号意味着,编译器会看§6.23(具体而言,“占位符语法匿名函数)和直接创建一个匿名函数。
An expression (of syntactic category Expr) may contain embedded underscore symbols _ at places where identifiers are legal. Such an expression represents an anonymous function where subsequent occurrences of underscores denote successive parameters.
在这种情况下,并在这部分的算法下,你的表达最终是这样的:
(x1: Int) => new Foo().foo(x1)
所不同的是微妙和,如通过@Antoras很好的解释,实际上只显示了的副作用的代码的存在。
请注意,正在进行一个bug修正涉及的情况下调用式的名称代码块(参见,例如, 这个问题 , 这个bug和这个bug )。
后记 :在两种情况下,匿名功能(x1:Int) => toto
被扩展到
new scala.Function1[Int, Int] {
def apply(x1: Int): Int = toto
}
我不能完全肯定,但我想,为什么是有区别的,就是Scala是不是纯粹的函数式编程语言的原因 - 它允许副作用:
scala> class Adder { var i = 0; def foo(a:Int)={i+=1;println(i);a+1} }
defined class Adder
scala> val curriedFunction = new Adder().foo _
curriedFunction: (Int) => Int = <function1>
scala> val anonymousFunction = new Adder().foo(_)
anonymousFunction: (Int) => Int = <function1>
scala> curriedFunction(5)
1
res11: Int = 6
scala> curriedFunction(5)
2
res12: Int = 6
scala> anonymousFunction(5)
1
res13: Int = 6
scala> anonymousFunction(5)
1
res14: Int = 6
匿名函数被视为:
val anonymousFunction = x => new Adder().foo(x)
而咖喱功能被视为:
val curriedFunction = {
val context = new Adder()
(a:Int) => context foo a
}
咖喱函数符合传统方式咖喱功能是在功能的语言处理:甲咖喱功能是被应用到一些数据,并评估该部分应用功能的功能。 换句话说:在一些数据的上下文中创建其被存储,并且可以稍后用于基于。 这正是curriedFunction
在做什么。 因为Scala允许可变状态的情况下可以改变 - 一个事实,可能会导致意外的行为,在这个问题可见。
纯象Haskell函数式语言没有这个问题,因为他们不允许这样的副作用。 在Scala中一个具有自己来确保由咖喱函数创建的上下文是真纯。 如果是这种情况并非如此,纯粹的咖喱函数的行为要求,匿名函数必须被使用,因为它们不存储上下文(可能会有问题,如果创设情境是昂贵的,必须经常做)。
因为它扩展到
(x: Int) => new Foo().foo(x)
所以,你只创建该实例的Foo
,当你调用该函数。
为什么第一个实例富马上的原因是因为它扩展到
private[this] val c: (Int) => Int = {
<synthetic> val eta$0$1: Foo = new Foo();
((a: Int) => eta$0$1.foo(a))
};
<stable> <accessor> def c: (Int) => Int = Foo.this.c;
而Foo
时抵达该处实例化一次,c定义。