On this page:
1.1 What Is This Assignment’s Purpose?
1.2 Loops
1.2.1 Python
1.2.2 Java
1.2.3 Racket
1.2.4 Task
1.3 Arrays
1.4 Additional Context
1 Loops🔗
1.1 What Is This Assignment’s Purpose?🔗

The following are programs that (a) many people find surprising or confusing, and (b) really exercise your understanding of SMoL (or force you to understand it better). Think of them as “poems”: pithy little programs that convey an important point.

1.2 Loops🔗

Imagine you are asked to write a program that implements a calculator. The core of this program is a panel of buttons corresponding to the digits 0 through 9. Each button, when pressed, should emit the corresponding digit (we ignore the rest of the interface and implementation for this assignment).

Consider the following pseudocode, which represents this idea:

create initial button_list

 

for i from 0 through 9:

  set button_list[i] to

    lambda: print i

 

# now simulate pushing the buttons

# which means invoking the stored callbacks

 

for i from 0 through 9:

  button_list[i]()

 

# this should print 0 through 9

Here are three natural transcriptions of this program. Some of these programs may produce errors or other surprising output. Before you ask: There are no typos in the following programs. Each has been carefully checked. They are behaving as we expect.

1.2.1 Python🔗

Make sure you run this in Python 3.

button_list = []

 

for i in range(10):

    button = lambda: print(i)

    button_list.append(button)

 

for button in button_list:

    button()

1.2.2 Java🔗

Make sure you run this in Java 1.8 or newer, and put this in a file called Buttons.java.

import java.util.*;

 

public class Buttons {

 

    public static interface Button {

        void press();

    }

 

    public static void main(String[] args) {

 

        List<Button>buttonList = new LinkedList<>();

 

        for(int i = 0; i < 10; i++) {

            // Uses Java8's new lambda syntax

            Button b = () -> System.out.println(i);

            buttonList.add(b);

        }

 

        for(Button b : buttonList) {

            b.press();

        }

    }

}

1.2.3 Racket🔗

#lang racket

 

(define button-list

  (for/list ([i (in-range 0 10)])

    (lambda () (println i))))

 

(for ([button button-list])

  (button))

1.2.4 Task🔗

Confirm that these programs look essentially equivalent, so they should all work essentially the same way.

Run these programs in Python, Java, and Racket respectively, and see what output they produce. For simplicity, you can run these programs directly on the Web:
What happens?

You should provide answers strictly in terms of what you have learned from the SMoL Tutors and Stacker.

1.3 Arrays🔗

On Twitter/X, Alex Wenzel (post has since been protected) describes being baffled by and spending a lot of time debugging the following Python code:

>>> d = [{}] * 2

>>> d

[{}, {}]

 

>>> d[0]["a"] = 1

>>> d[1]["a"] = 2

>>> d

[ {'a': 2}, {'a': 2} ]

Explain (again, using only concepts from the SMoL Tutors and Stacker) what is going on in terms of SMoL.

1.4 Additional Context🔗

The looping issue has been recognized in other programming languages too. Here’s a discussion about plans to do something about it in the Go programming language, where they call it the “loop variable problem”. See also the response that C# had already made this change, in part because the “standard” behavior made people think there was a compiler error!

These posts also make clear how hard it is to change the language once it picks the “standard” behavior: something to think about for future language designers. In the Go case, though, the problem is sufficiently important that they are fixing the behavior, calling it “one of the most common Go mistakes”. See the comments even by experienced programmers on the site discussing the above proposal.