为什么在Scala并部分应用功能延迟类的实例?(Why does partially applied

2019-09-17 13:21发布

想象一下这样的代码:

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

为什么埃塔扩张与局部的功能应用使类的实例有区别吗?

Answer 1:

男孩,这是一个微妙的,但据我可以告诉它下面的斯卡拉符合规范彻底。 我会从规范的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
}


Answer 2:

我不能完全肯定,但我想,为什么是有区别的,就是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中一个具有自己来确保由咖喱函数创建的上下文是真纯。 如果是这种情况并非如此,纯粹的咖喱函数的行为要求,匿名函数必须被使用,因为它们不存储上下文(可能会有问题,如果创设情境是昂贵的,必须经常做)。



Answer 3:

因为它扩展到

(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定义。



文章来源: Why does partially applied function defer class instantiation in Scala?
标签: scala