Bistro Blocks

Blocks are a very powerful part of the Smalltalk language. They are so important, Bistro not only retains the block concepts of Smalltalk, but also extends them in thoughtful ways to improve integration with Java™. Blocks are so flexible, Bistro uses them for a wide variety of language features, including decision structures, collection iteration, exception handling, multithreading, and event handling. Table 5 lists the various block idioms supported by Bistro.

Decision Structures

There are no reserved words for decision structures in Bistro like there are in languages such as Java™ and C++. Instead, decision structures use message idioms that combine Boolean expressions with Blocks. Table 5 lists some of the most commonly used decision idioms as they are expressed in Bistro. Notice that the decision idioms do not include a switch or a case statement. There are a number of ways to mimic such a control structure in Bistro. However, it is generally discouraged in favor of object-oriented designs that make use of classes and polymorphism to distinguish and handle the separate cases.

Many of the decision structures identified in Table 5 can be optimized during translation to Java™. Often, they can be translated directly into equivalent Java™ decision structures. Similar optimizations are often performed by commercial Smalltalk compilers. However, under certain circumstances, these control structures and other custom blocks are best implemented using Java™ inner classes.

Table 5. Bistro Block Idioms
Decisions booleanExpression or: [ booleanExpression ]
booleanExpression and: [ booleanExpression ]
booleanExpression ifTrue: [ "..." ].
booleanExpression ifTrue: [ "..." ] ifFalse: [ "..." ].
booleanExpression ifFalse: [ "..." ].
booleanExpression ifFalse: [ "..." ] ifTrue: [ "..." ].
[ booleanExpression ] whileTrue: [ "..." ].
[ booleanExpression ] whileFalse: [ "..." ].
[ "..." booleanExpression ] whileTrue.
[ "..." booleanExpression ] whileFalse.
Iterators "Indexed"
start to: end do: [ :index | "..." ].
start to: end by: increment do: [ :index | "..." ].
aCollection do: [ :element | "..." ].
aCollection collect: [ :element | "..." ].
aCollection select: [ :element | "..." ].
aCollection reject: [ :element | "..." ].
aCollection detect: [ :element | "..." ].
aCollection detect: [ :element | "..." ] ifNone: [ "..." ].
inject: initialValue
into: [ :aValue :element | "..." ].
Evaluations [ :a :b | "..." ] value: x value: y.
[ :a | "..." ] value: x.
[ "..." ] value.
Exceptions [ "..." ] ifCurtailed: [ "... catch any exception ..." ].
[ "..." ] ensure: [ "... evaluated finally ..." ].
[ "..." ] "maps to try {} catch {} finally {}"
catch: [ :exception (ExceptionClass) | "..." ]
"... additional catch blocks ..."
ensure: [ "... evaluated finally ..." ].
Threads "creating threads"
[ "..." ] fork.
[ "..." ] forkAt: aPriority.
"synchronizing threads"
anObject acquireMonitorDuring: [ "..." ].
anObject notifyWaitingThread.
anObject notifyAllWaitingThreads.
anObject waitForChangeIfInterrupted: [ "..." ].
waitForChange: millisecondDuration
ifInterrupted: [ "..." ].
Adapters SomeInterface asNew: [ "..." ]

Implementing Blocks with Inner Classes

Java™ inner classes make duplicating the semantics of Smalltalk blocks rather easy. Blocks can be implemented by translation into appropriate anonymous inner classes. By defining a few abstract base classes, Bistro provides the foundations for deriving these anonymous inner classes. Some Smalltalk implementations support an arbitrary number of block arguments. To keep the translation mapping simple, Bistro currently limits support for blocks to those that take 0, 1, or 2 arguments. Table 6 lists the abstract base classes for blocks included in Bistro and an example of the kind of block each supports.

Table 6. Bistro Block Base Classes
ZeroArgumentBlock [ "..." ] value
OneArgumentBlock [ :a | "..." ] value: x
TwoArgumentBlock [ :a :b | "..." ] value: x value: y

The following method provides a simple example of a block commonly used for sorting the elements in a SortedCollection. The Bistro compiler translates the block returned by this method into an instance of an anonymous Java™ inner class derived from TwoArgumentBlock.

"Returns a block used to sort the elements of a collection.
A pair of arguments is compared to determine whether the
first argument is less than or equal to the second argument."
^[ :a :b | a <= b ]

Bistro supports optional typing, including method and block results and arguments. When translating typed block arguments to Java™, the Bistro compiler uses type erasure to generate a generalized wrapper method in the inner class, as well as a typed method that contains the block code. The following example revisits the sortBlock method, this time with a typed block result and typed block arguments.

"Returns a block used to sort the elements of a String collection.
A pair of Strings is compared to determine whether the first String
argument is less than or equal to the second String argument."
^[ (Boolean) :a (String) :b (String) | a <= b ]

Local Block Variables

Bistro local block variables can be conveniently mapped (when needed) to instance variables in Java™ inner classes. The following method includes a block with a local variable. The local block variable uses italic to help identify it.

"Returns the non-nil elements from (anArray)."
extantElementsFrom: anArray
result := OrderedCollection new.
1 to: anArray size do: [ :index |
"-->" element := anArray at: index.
element isNil ifFalse: [ result add: element ]
^result asArray

Methods Returns from Blocks

Like Smalltalk, Bistro supports the ability to return method results directly from inside nested blocks using a message expression that begins with a caret (^). Method returns exit all enclosing block scopes, including the enclosing method scope. The following search: method provides an example of this feature. If the method finds element in the searchTargets, it returns the element as the result of the method. Note that in this case, the method result is returned by an exit from a nested block scope.

"Returns the first element of (aCollection)
contained in the (searchTargets)."
search: aCollection for: searchTargets
aCollection do: [ :element |
(searchTargets includes: element)
ifTrue: [ ^element ] "<-- method exit"

When the Bistro compiler cannot optimize a block inside a method and uses an inner class to implement it, special consideration must be given to the method returns. Bistro implements such method returns using the Java™ exception facility. Bistro uses an instance of MethodResult to carry a result value across the scopes of the intervening inner classes. MethodResult is a subclass of RuntimeException. So, it does not impact the method signatures of the inner classes used to implement the block scopes. Bistro wraps the method result value in an instance of MethodResult, throws the MethodResult from the inner block scope and catches the MethodResult once it reaches the enclosing method scope. Then, the MethodResult is caught and unwrapped to return the result value.


As of release JDK 1.1, the Java™ event model changed. The new event model uses interfaces and inner classes to provide a more flexible mechanism for handling events. In order to integrate easily with the new event model, the Bistro compiler recognizes a special message idiom to support the definition of event handlers as anonymous inner classes. The following code fragment provides an example of how this special idiom (asNew:) can be used to define an ActionListener that handles a button click.

"Attach an action listener to the closeButton."
closeButton addActionListener:
"The listener interface is implemented by a handler instance."
java.awt.event.ActionListener asNew:
"Define the event handler for the closeButton click event."
(void) actionPerformed: event (java.awt.event.ActionEvent)
[ "... handle the click event from the closeButton ..."

Java™ is a trademark of Sun Microsystems, Inc.

Permission is granted to copy this document provided this copyright statement is retained in all copies.
Copyright 1999-2001 Nikolas S. Boyd.