Why is the deferred class function partially applied in Scala?

Imagine this code:

class Foo { println("in Foo") def foo(a: Int) = a + 1 } 

Now, if we call:

 new Foo().foo _ 

an instance of the Foo class will be created, as expected:

 in Foo res0: (Int) => Int = <function1> 

However, if we call this:

 new Foo().foo(_) 

The Foo constructor will not be called:

 res1: (Int) => Int = <function1> 

If we then say:

 res1(7) 

when foo gets an instance:

 in Foo res2: Int = 8 

Why does an Eta application or partial function application matter in instantiating a class?

+6
source share
3 answers

Boy, this is subtle, but as far as I can tell it is completely Scala spec completely. I will quote from version 2.9 of the specification.

In the first example: as you say correctly, you see the eta extension through the special case of the method value (§6.7):

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.

The eta extension algorithm is given in 6.26.5, which you can use to get the following replacement for the expression new Foo().x1 _ :

 { val x1 = new Foo(); (y1: Int) => x1.(y1); } 

This means that when the eta extension is used, all subexpressions are evaluated at the point where the transformation occurs (if I correctly understood the meaning of the phrase "maximum subexpression") and the final expression is to create an anonymous function.

In your second example, these additional parentheses mean that the compiler will consider §6.23 (in particular, “Column Syntax for Anonymous Functions”) and create the anonymous function directly.

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.

In this case, following the algorithm in this section, your expression ends with the following:

 (x1: Int) => new Foo().foo(x1) 

The difference is subtle and, as @Antoras explains well, actually only shows if there is a side code.

Please note that for the case associated with blocks of code by name, there is a fix (see, for example, this question , this error and this error ).

Postscript In both cases, the anonymous function (x1:Int) => toto expands to

 new scala.Function1[Int, Int] { def apply(x1: Int): Int = toto } 
+2
source

I'm not quite sure, but I think the reason there is a difference is because Scala is not a purely functional programming language - it allows side effects:

 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 

An anonymous function is considered as:

 val anonymousFunction = x => new Adder().foo(x) 

While the curried function is processed as:

 val curriedFunction = { val context = new Adder() (a:Int) => context foo a } 

The curried function follows the traditional way of working with italics in functional languages: the curried function is a function that applies to some data and evaluates this partially applicable function. In other words: based on some data, a context is created that is stored and can be used later. This is exactly what curriedFunction does. Because Scala allows a volatile state, the context can be changed - a fact that can lead to unexpected behavior, as seen in the question.

Purely functional languages ​​such as Haskell do not have this problem because they do not tolerate such side effects. In Scala, you need to make sure that the context created by the curries function is really clean. If this is not the case and the behavior of purely currency functions is required, it is necessary to use anonymous functions, since they do not store the context (which can be problematic if creating the context is expensive and must be performed often).

+2
source

As it expands to

 (x: Int) => new Foo().foo(x) 

So, you call this instance of Foo only when you call this function.

And the reason the first instance of Foo appears right away is because it expands to

 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; 

And Foo gets the instance here as soon as c is determined.

+1
source

All Articles