teaching machines

CS 330: Lecture 32 – Closures

April 25, 2018 by . Filed under cs330, lectures, spring 2018.

Dear students,

Let’s start with a Program This! Consider the foreach algorithm:

for each item in list
  do something with that item (call a method, print, etc.)

The core idea is that we have a list of items, and we want to produce some side effect for each of them. The foreach algorithm doesn’t return anything. It just does stuff. (The Haskell implementation of this algorithm is mapM_, which we didn’t discuss.)

Implement this in Java. First, write a functional interface (SAM), and then write a higher-order function that implements the algorithm.

Here’s my solution.
interface Visitor<T> {
  void visit(T x);
}

class Library {
  // ...

  public static <T> void foreach(List<T> list,
                                 Visitor<T> visitor) {
    for (T item : list) {
      visitor.visit(item);
    }
  }
}

We can use foreach to quickly print out a list of planets, for example:

Library.foreach(planets, p -> System.out.print(p));

In Haskell, we saw that it’s very easy to write a lambda that does no real work—it simply wraps around another function. If it’s really that other function we want to call, we should just pass that function off instead of unnecessarily wrapping it up in another function. Can we do that in Java 8? Yes, sometimes. We can use method references:

List<String> names = Library.map(planets, Planet::getName);
System.out.println(names);
Library.foreach(planets, System.out::print);

There are a lot of related but separable ideas in this stage of our journey. Let’s take a moment to revisit these:

There are some issues surrounding lambdas that I think we need to address because I think you’re going to run into them in your development life. That’s how we’ll spend the rest of our time today. First, checkout this HTML and Javascript code, which renders some buttons and then grabs some handles to them:

<input id="buttonA" type="button" value="Click A!">
<input id="buttonB" type="button" value="Click B!">
<input id="buttonC" type="button" value="Click C!">

<script>
var buttons = [
  document.getElementById('buttonA'),
  document.getElementById('buttonB'),
  document.getElementById('buttonC')
];
</script>

Let’s loop through these. What do you expect for output here?

for (var i = 0; i < buttons.length; ++i) {
  console.log(i);
}

0, 1, 2, right? Right. How about this?

for (var i = 0; i < buttons.length; ++i) {
  console.log(buttons[i]);
}

It shows the buttons, as we’d expect. Now let’s register a click handler:

for (var i = 0; i < buttons.length; ++i) {
  buttons[i].onclick = function() {
    alert(i);
  }
}

Pretend we want to do something that depends on the button’s serial number. We’ll use alert as a placeholder for that more meaningful computation. This code does not behave as we’d expect. All three buttons produce a 3. There wasn’t even a button 3.

What’s happening here? We are seeing the effect of a closure. In short, a closure is made when we define a lambda. Not only does the code get stored, but so does the relevant surrounding scope. The function references i, which is the exact same i that was declared in our outer loop. All three of our lambdas hold a reference to i, and so does the surrounding scope. The surrounding scope keeps incrementing i, and this affects the lambdas.

This automatic capturing of the surrounding environment is often a wonderful thing. In fact, I’d go so far as to say that lambdas need closure semantics to be useful. Suppose for a moment that lambdas could not access variables from the surrounding scope. Consider writing a compound slider/spinner widget in HTML:

<input id="slider" type="range">
<input id="ticker" type="number">

When you change one of the widgets, you want the other widget to change to stay in sync, so you write this code:

var slider = document.getElementById('slider');

slider.oninput = function() {
  // ?
}

How do you update the ticker? In the absence of closures, the only information we have is what’s given to our lambda through parameters. Only the information known and deemed worthy by the writer of the event system will ever get sent to our callbacks. In this case, we do get some information sent to our callback:

slider.oninput = function(event) {
  console.log(event);
}

But it’s not enough to update the ticker. Instead, we do have closure semantics and we can access variables from the surrounding scope:

var slider = document.getElementById('slider');
var ticker = document.getElementById('ticker');

slider.oninput = function(event) {
  ticker.value = slider.value; 
}

And vice versa:

ticker.oninput = function(event) {
  slider.value = ticker.value; 
}

So, we do want to capture the surrounding scope, but how do we deal with the situation we saw with our buttons where the closed-over i was too shared? Well, we need to narrow the scope. Let’s try declaring num inside the loop:

for (var i = 0; i < buttons.length; ++i) {
  var num = i;
  buttons[i].onclick = function() {
    alert(num);
  }
}

Now we see 2 on every click. Argh. This solution actually does work in some languages, but not Javascript. In this blessed language, variable declarations are hoisted to the top of their scopes. A loop doesn’t introduce a new scope in Javascript, so the top of the scope is the same as the beginning of our script. Effectively, our code becomes this:

var num;

// ...

for (var i = 0; i < buttons.length; ++i) {
  num = i;
  buttons[i].onclick = function() {
    alert(num);
  }
}

That’s still too much sharing for our buttons. We need some other trick to introduce a narrower scope. What in Javascript does provide a new scope? Functions. We can falsely wrap up the generation of the lambda inside another function call:

for (var i = 0; i < buttons.length; ++i) {
  function register(iUnshared) {
    buttons[iUnshared].onclick = function() {
      alert(iUnshared);
    }
  }
  register(i);
}

This situation happens frequently enough in Javascript that there’s a slightly simpler idiom called an immediately invoked function expression (IIFE). It looks kind of gross at first, but it eliminates the need to bind the function to a name:

for (var i = 0; i < buttons.length; ++i) {
  (function(iUnshared) {
    buttons[iUnshared].onclick = function() {
      alert(iUnshared);
    }
  })(i);
}

iUnshared only exists in the scope of the function, so we will have no unintended sharing across buttons.

We could also have fixed this particular example by not using a for loop in the first place. A forEach HOF would also introduce a new scope for the index variable:

buttons.forEach((button, i) => alert(i));

But we’re bound to run into oversharing situations sooner or later, and it’s important that we can read and write IIFEs.

Here’s your TODO list:

See you next time!

Sincerely,

P.S. It’s time for a haiku!

Code is us, dancing
I leave holes and you fill them
Hold me closure, dear

P.P.S. Here’s the code we wrote together:

Whatdo.java

public interface Whatdo<A> {
  void doer(A item);
}

ForeachTest.java

import java.util.Arrays;
import java.util.Random;
import java.util.List;

public class ForeachTest {
  public static <A> void foreach(List<A> list, Whatdo<A> action) {
    for (A item : list) {
      action.doer(item);
    }
  }

  public static void except(String s) {
    Random g = new Random();
    System.out.println(s + " " + g.nextInt(100));
  }

  public static void main(String[] args) {
    String[] names = {"Jordan", "Jones", "Null", "Andy", "Dang", "Dayne"};
    /* foreach(Arrays.asList(names), item -> System.out.println(item)); */
    /* foreach(Arrays.asList(names), System.out::println); */
    foreach(Arrays.asList(names), ForeachTest::except);
  }
}