CSC447

Concepts of Programming Languages

Closure Implementation

Instructor: James Riely

Closures and Mutability

  • Recall javac requires final i from enclosing scope
    
    for (int i = 0; i < 5; i++) { 
      new Thread (new Runnable () {
          public void run () { /* rejected: i mutates */
            while (true) { System.out.print (i); }
          }
        }).start ();
    }
                  
  • So a copy is made
    
    for (int i = 0; i < 5; i++) { 
      int x = i; 
      new Thread (new Runnable () {
          public void run () { /* accepted: x never mutates */
            while (true) { System.out.print (x); }
          }
        }).start ();
    }
                  

Closures and Mutability

  • The same holds for lambda expressions
    
    for (int i = 0; i < 5; i++) { 
      new Thread (() -> { /* rejected: i mutates */
            while (true) { System.out.print (i); }
          }).start ();
    }
                  
  • So a copy is made
    
    for (int i = 0; i < 5; i++) { 
      int x = i; 
      new Thread (() => { /* accepted: x never mutates */
            while (true) { System.out.print (x); }
        }).start ();
    }
                  

Closures and Mutability

  • Scala allows closures over mutable variables!
    
    var i = 1
    while i < 5 do
      Thread (() => 
        while true do print (i)
      ).start
      i = i + 1
                  
  • We really need a copy...
    
    var i = 1
    while i < 5 do
      val x = i
      Thread (() => 
        while true do print (x)
      ).start
      i = i + 1
                  

Closures and Mutability

  • Immutable programming is better!
    
    def main(args: Array[String]): Unit =
      for i <- (1 to 4) do
        Thread (() => 
          while true do print (i)
        ).start
                  

Why is this tricky?

  1. enclosing function outer is called
    • AR contains data x
  2. outer returns nested function inner
    • inner references x from outer's AR
    • lifetime of outer's AR and x ends
  3. nested function inner is called
    • needs x from outer's AR

def outer (x:A) : B=>C = 
  def inner (y:B) : C = 
    //...use x and y...
  inner
          

Implementation: Closures

  • Closures store inner function and environment
  • Environment contains variables from enclosing scope
  • Lifetime of environment = lifetime of inner function
    • environment is allocated on the heap
  • Different implementations in different PLs
  • Recurring implementation choice: copy or share?

Implementation Choice

  • Closure contains
    • pointer/reference to code for inner
    • a copy of x

def outer (x:A) : B=>C = 
  def inner (y:B) : C = 
    ...use x and y...
  inner
          

Implementation Choice

  • Closure contains
    • pointer/reference to code for inner
    • copies of x and u
  • inner sees updated u?
  • Require u to be immutable?

def outer (x:A) : B=>C = 
  var u:A = x
  def inner (y:B) : C = 
    //...use u and y...
  u = u + 1
  inner
          

Implementation Choice

  • Alternatively, share u
  • Closure contains
    • pointer/reference to code for inner
    • copy of x
    • reference to shared u (on heap)

def outer (x:A) : B=>C = 
  var u:A = x
  def inner (y:B) : C = 
    //...use u and y...
  u = u + 1
  inner
          

Scala2.12 Implementation


object Closure:
  def outer (x:Int) : Boolean=>Int = 
    def inner (y:Boolean) : Int = 
      x + (if y then 0 else 1)
    inner
          

$ scalac Closure.scala

$ ls -1 Closure*
Closure$$anonfun$outer$1.class
Closure.class
Closure$.class
Closure.scala
          

Scala2.12 Implementation

  • The closure is an instance of the second class
  • x copied into field x$1

$ javap -p Closure
Compiled from "Closure.scala"
public final class Closure {
  public static scala.Function1<java.lang.Object, java.lang.Object> outer(int);
}

$ javap -p Closure\$\$anonfun\$outer\$1
Compiled from "Closure.scala"
public final class Closure$$anonfun$outer$1 extends scala.runtime.AbstractFunction1<java.lang.Object, java.lang.Object> {
  private final int x$1;
  public final int apply(boolean);
  public Closure$$anonfun$outer$1(int);
}
          
(some parts removed)

Scala2.12 Implementation

  • u is a var declaration, so is mutable

object Closure:
  def outer (x:Int) : Boolean=>Int =
    var u:Int = x
    def inner (y:Boolean) : Int =
      x + u + (if y then 0 else 1)
    inner
          

Scala2.12 Implementation

  • x copied into field x$1
  • u shared on heap via reference in field u$1

$ javap -p Closure\$\$anonfun\$outer\$1
Compiled from "Closure.scala"
public final class Closure$$anonfun$outer$1 extends scala.runtime.AbstractFunction1<java.lang.Object, java.lang.Object> {
  private final int x$1;
  private final scala.runtime.IntRef u$1;
  public final int apply(boolean);
  public Closure$$anonfun$outer$1(int, scala.runtime.IntRef);
}
          
(some parts removed)

Further reading

  • There are some great resources about closures in the javascript world, such as this