Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198
» CS 330: Lecture 33 – Closures Continued

teaching machines

CS 330: Lecture 33 – Closures Continued

April 27, 2018 by . Filed under
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
cs330, lectures, spring 2018.

Dear students,

Last time we ended in the middle of a discussion about the dangers of closures oversharing values. We fixed the situation by adding a function to introduce a new scope for each closure so they wouldn’t clobber each others’ state. 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.

This automatic capturing of the surrounding environment in a closure 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, a closure is a chunk of unevaluated code packaged up with its state. Isn’t that a crazy idea? Have you ever seen anything else like this—this marriage between code and data?

You have. Objects. In fact, if you ever find yourself in a language that lacks one of them, you can use the other to implement the missing feature. Javascript, for instance, doesn’t have objects in the same sense that Java does. But since it supports closures, we can add them.

In general, an object is a set of methods that share instance variables. To implement this in Javascript, we need a set of closures that share state. No one else should be able to access that state, so we’ll need a function to introduce a new scope. That function will serve as the “constructor”:

function Hero(name, hp, mp) {
  // ...
}

Each Hero should be a collection of closures. We’ll just stick to functions for curing the Hero. We’ll put all our closures in a dictionary:

function Hero(name, hp, mp) {
  var object = {};

  object.cure = function() {
    hp += 10;
    mp -= 5;
  }

  object.cura = function() {
    hp += 30;
    mp -= 10;
  }

  object.curaga = function() {
    hp += 100;
    mp -= 30;
  }

  return object;
}

First, note that this function isn’t really a constructor in the traditional sense. It might as well have been named createHero. Second, notice that all the lambdas are accessing the same shared state. I didn’t need to assign instance variables. The closure semantics automatically “assign” them. Third, we don’t really have an object here. We just have a dictionary.

Let’s add another function to help us test our object:

object.log = function() {
  console.log(name + ' has ' + hp + ' HP and ' + mp + ' MP.');
}

We can create our object by invoking the instructor and then call methods on it:

var zorn = Hero();
zorn.log();
zorn.cure();
zorn.log();
zorn.cura();
zorn.log();
zorn.curaga();
zorn.log();

And there we have objects in Javascript! All we’re really doing here, though, is making function calls. There’s no this struct being passed around. The functions themselves have the data in their closure. zorn itself does not nothing but hold the functions.

There are some better ways for emulating objects in Javascript, including ways that provide a form of inheritance. I encourage you to read up prototypal inheritance if you are interested.

Now let’s consider closures in Java. Let’s print out a numbered list of planets using a lambda:

int i = 1;
Library.foreach(planets, p -> {
  System.out.printf("%d. %s%n", i, p.getName());
  i += 1;
});

But this fails to compile because Java doesn’t truly support closures. Inner classes and lambdas do not truly share bindings with outer scopes. Rather, the inner construct gets an independent copy of the outer scope’s binding. And to emphasize that the two bindings are separate, that a change in one will not result in a change in the other, the compiler says that you can’t change them at all—either of them. They must be final:

final int i = 1;
Library.foreach(planets, p -> {
  System.out.printf("%d. %s%n", i, p.getName());
  // i += 1; 
});

Sadly, final variables can’t be changed. So much for our numbered list! We have a few options:

Let’s investigate the last of these. Consider this attempt at firing off 10 threads:

public static void main(String[] args) {
  for (int i = 0; i < 10; ++i) {
    new Thread(() -> System.out.println(i)).start();
  }
}  

First, it is reasonable for a thread to know its serial number. For example, thread i might process task i or chunk i of some large dataset. Second, this code doesn’t compile. Variable i is not final, and therefore by the rules of Java cannot be closed over. But the loop body presents us an opportunity for a new scope:

for (int i = 0; i < 10; ++i) {
  int iThread = i;
  new Thread(() -> System.out.println(iThread)).start();
}

A local variable is enough to appease the compiler. There’s no oversharing between threads.

In summary, lambdas promise brevity and simplicity for working with higher-order functions, but different languages approach sharing and closures differently. Haskell, incidentally, avoids all the concerns of oversharing because variables are immutable.

Here’s your TODO list:

See you next time!

Sincerely,

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

Data married code
They’d been an item object
Now there’s some closure

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

buttons.html

<!DOCTYPE html>
<html>
<head>
  <title>...</title>
</head>
<body>
  <input id="button0" type="button" value="Click A!"> 
  <input id="button1" type="button" value="Click B!"> 
  <input id="button2" type="button" value="Click C!"> 

  <script>
//var j;

var buttons = [
  document.getElementById('button0'),
  document.getElementById('button1'),
  document.getElementById('button2'),
];

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

Array.prototype.forAndy = function(whatdo) {
  for (var i = 0; i < this.length; ++i) {
    whatdo(this[i], i); 
  }
}

//buttons.forAndy(function(button, i) {
//  button.onclick = () => alert(i);
//});

buttons.forAndy((button, i) => {
  button.onclick = () => alert(i);
});

  </script>
</body>
</html>

slick.html

<!DOCTYPE html>
<html>
<head>
  <title>...</title>
</head>
<body>
  <input type="range" id="slider">
  <input type="number" id="ticker">

  <script>
var slider = document.getElementById('slider');
var ticker = document.getElementById('ticker');

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

ticker.oninput = function(e) {
  slider.value = ticker.value;
}
  </script>
</body>
</html>

hero.js

function Hero(name, hp, mp, clazz, race) {
  var object = {};

  // object = {
    // cure: function() {
      // ...
    // },

    // cura: function() {
      // ...
    // }
  // }

  object.cure = function() {
    hp += 10;
    mp -= 7;
  }

  object.cura = function() {
    hp += 50;
    mp -= 27;
  }

  object.curaga = function() {
    hp += 100;
    mp -= 47;
  }

  object.log = function() {
    console.log(name + " has " + hp + " HP, " + mp + " MP.");
  }

  return object;
}

var zorn = Hero('Zorn', 7000, 842, 'Computer Scientist', 'Elf');
zorn.log();
zorn.cure();
zorn.log();

Planet.java

public class Planet {
  private String name;
  private double ausFromTheSun;
  private double relativeMass;

  public Planet(String name,
                double ausFromTheSun,
                double relativeMass) {
    this.name = name;
    this.ausFromTheSun = ausFromTheSun;
    this.relativeMass = relativeMass;
  }

  public String getName() {
    return name;
  }

  public double getAUs() {
    return ausFromTheSun;
  }

  public double getMass() {
    return relativeMass;
  }

  public String toString() {
    return String.format("%15s %6.2f %6.2f%n", name, ausFromTheSun, relativeMass);
  }
}

Whatdo.java

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

ForeachTest.java

import java.util.Arrays;
import java.util.List;
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 main(String[] args) {

    for (int i = 0; i < 10; ++i) {
      int j = i;
      new Thread(() -> System.out.println(j)).start();
    }

    /* List<Planet> planets = Arrays.asList( */
       /* new Planet("Saturn", 9.5, 95.0), */
       /* new Planet("Earth", 1.0, 1.0), */
       /* new Planet("Venus", 0.7, 0.815), */
       /* new Planet("Jupiter", 5.2, 318.0), */
       /* new Planet("Uranus", 19.6, 14.0), */
       /* new Planet("Neptune", 30.0, 17.0), */
       /* new Planet("Mars", 1.5, 0.107), */
       /* new Planet("Mercury", 0.4, 0.055) */
      /* );  */

    /* final int number = 0; */
    /* foreach(planets, planet -> { */
      /* System.out.print(number + ". " + planet); */
      /* ++number; */
    /* }); */
  }
}