5.4 Block syntax

Blocks provide a mechanism to defer the evaluation of expressions. A block is essentially an anonymous function. A block is evaluated by sending it the message #value. The block answers the value of the last expression in its body, unless there is an explicit return (with ^), in which case it returns the value of the subsequent expression).

[ 1 + 2 ] value
⇒ 3

Blocks may take parameters, each of which is declared with a leading colon. A vertical bar separates the parameter declaration(s) from the body of the block. To evaluate a block with one parameter, you must send it the message #value: with one argument. A two-parameter block must be sent #value:value:, and so on, up to 4 arguments:

[ :x | 1 + x ] value: 2
⇒ 3
[ :x :y | x + y ] value: 1 value: 2
⇒ 3

If you have a block with more than four parameters, you must use #valueWithArguments: and pass the arguments in an array. (A block with a large number of parameters is often a sign of a design problem.)

Blocks may also declare local variables, which are surrounded by vertical bars, just like local variable declarations in a method. Locals are declared after any arguments:

[ :x :y | | z | z := x + y. z ] value: 1 value: 2
⇒ 3

Blocks can refer to variables of the surrounding environment. Blocks are said to “close over” their lexical environment, which is a fancy way to say that they remember and refer to variables in their surrounding lexical context – those apparent in their enclosing text.

The following block refers to the variable x of its enclosing environment:

|x|
x := 1.
[ :y | x + y ] value: 2
⇒ 3

Blocks are instances of the class BlockClosure. This means that they are objects, so they can be assigned to variables and passed as arguments just like any other object.

Consider the example below to compute the divisors of an integer:

| n m |
n := 60.
m := 45.
(1 to: n) select: [:d | n \\ d = 0 ].
"⇒ #(1 2 3 4 5 6 10 12 15 20 30 60)"
(1 to: m) select: [:d | m \\ d = 0]
"⇒ #(1 3 5 9 15 45)"

Example 5.2: Compute divisors

The problem with this example is the code duplication in the divisor computation. We can avoid duplication with a dedicated block doing the computation and assigning it to a variable:

 CuisLogo How will you rewrite Example 5.2 to avoid code duplication?

Exercise 5.1: Block to compute divisors

The SpaceWar>>teleport: method contains a nice example using a block to avoid code duplication to generate random abscissa and ordinate coordinates. Each time a new coordinate is needed, the message #value is sent to the block of code:

SpaceWar>>teleport: aShip
  "Teleport a ship at a random location"
  | area randomCoordinate |
  aShip resupply.
  area := self morphLocalBounds insetBy: 20.
  randomCoordinate := [(area left to: area right) atRandom].
  aShip 
    velocity: 0 @ 0;
    morphPosition: randomCoordinate value @ randomCoordinate value

Example 5.3: teleport: method