Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Scala maintains the values of variable when the closure was defined?

Tags:

closures

scala

Does scala maintains the values of variable by copy or reference?

For example, in Ruby "the closure will actually extend the lifetime of all the variables that it needs. It will not copy them, but will retain a reference to them and the variables themselves will not be eligible for garbage collection (if the language has garbage collection) while the closure is around". [SKORKIN]

like image 852
Billz Avatar asked Jul 25 '12 20:07

Billz


People also ask

What is the use of closure function in Scala?

Scala Closures are functions which uses one or more free variables and the return value of this function is dependent of these variable. The free variables are defined outside of the Closure Function and is not included as a parameter of this function.

On which value closure will depend?

The choice of closure type depends entirely on the degree of drainage, the existing amount of tissue for wound closure and infection status at the site of wound closure.

Is Scala pass by value or reference?

Java and Scala both use call by value exclusively, except that the value is either a primitive or a pointer to an object. If your object contains mutable fields, then there is very little substantive difference between this and call by reference.

What is spark closure?

Delta Lake with Apache Spark using Scala A closure is a function, whose return value depends on the value of one or more variables declared outside this function. The following piece of code with anonymous function. val multiplier = (i:Int) => i * 10.


2 Answers

The jvm has no closures, it has only object. The scala compiler generate anonymous classes implementing the appropriate Function trait (depending of the argument and result type of the signature) for each occurence of a closure in the code.

For instance, if for some l : List[Int], you write l.map(i => i + 1), it will be converted to

class SomeFreshName extends Function[Int, Int] {
  def apply(i: Int) = i + 1
}

l.map(new SomeFreshName())

In this case, there is no real closure, as in i => i + 1, there is no free variable, only the argument i and constant.

If you close over some local vals, or equivalently a parameter of the function, they will have to be passed as constructor parameter to the closure-implementing class :

for l.map(i => s + i) where s is a string parameter or a local of the method, it will do

class SomeFreshName(s: String) extends Function[Int, String] {
  def apply(i: Int) = s + i
}
l.map(new SomeFreshName(s))

passing as many parameter in the constructor as needed.

Note : If s was a field of the class instead of a local of the method, then s + i would be in fact this.s + i, and this would be passed to anonymous class.

There is nothing special in the garbage collector (again, the jvm does not know of closures), simply, as the closure object has a reference to s, s will live at least as long as the closure object.

Note that exactly the same thing happens in the java language with anonymous classes. When an anonymous class uses locals of the enclosing method, those locals are silently added as fields of the anonymous classes, and passed at constructor.

In java, this is allowed only if the local are final, which is equivalent of scala val, as opposed to var.

Indeed, with this implementation, as soon as the closure is created, it has its own copy of the variable it closes other. If it modifies them, those modification will not be reflected in the method. If they are modified in the closure, this will not be reflected in the method.

Suppose you write

var i = 0
l.foreach{a => println(i + ": " + a); i = i + 1}
println("There are " + i + " elements in the list")

The implementation described before would be

class SomeFreshName(var i: Int) extends Int => Unit {
   def apply(a: Int) = println(i + ": " + a); i = i + 1
}
var i = 0
l.foreach(new SomeFreshName(i)
println("There are " + i + " elements in the list") 

Doing that, there would be two variable i, the one in the method, and the one in SomeFreshName. Only the one in SomeFreshName would be modified, and the last println would always report 0 elements.

Scala solve his problem by replacing var taken in the closure by reference objects. Given a class

class Ref[A](var content: A)

the code is first replaced by

val iRef = new Ref[Int](0)
l.foreach{a => 
  println(iRef.content + ": " + a); 
  iRef.content += iRef.content + 1
}
println("There are " + i + " elements in the list")

This is done of course only to var that happens to be taken by a closure, not to every var. Doing that, the var has been replaced by a val, the actual variable value has been moved into the heap. Now, the closure can be done as usual, and it works

class SomeFreshName(iRef: Ref[Int]) ...
like image 118
Didier Dupont Avatar answered Sep 28 '22 16:09

Didier Dupont


Closures in Scala also don't deep copy objects, they'll only keep a reference to the object. Moreover, a closure does not get it's own lexical scope, but instead, it uses the surrounding lexical scope.

class Cell(var x: Int)
var c = new Cell(1)

val f1 = () => c.x /* Create a closure that uses c */

def foo(e: Cell) = () => e.x
  /* foo is a closure generator with its own scope */

val f2 = foo(c)    /* Create another closure that uses c */

val d = c          /* Alias c as d */
c = new Cell(10)   /* Let c point to a new object */
d.x = d.x + 1      /* Increase d.x (i.e., the former c.x) */

println(f1())      /* Prints 10 */
println(f2())      /* Prints 2 */

I can't comment on garbage collection, but I assume that the JVM's garbage collector will not remove objects that are referenced by a closure, as long as the closure is still referenced.

like image 20
Malte Schwerhoff Avatar answered Sep 28 '22 15:09

Malte Schwerhoff