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.
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
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|
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|
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
"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 ]
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)."
result := OrderedCollection new.
1 to: anArray size do: [ :index |
"-->" element := anArray at: index.
element isNil ifFalse: [ result add: element ]
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
MethodResult, throws the
MethodResult from the inner block scope and catches
MethodResult once it reaches the enclosing method scope. Then, the
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."
"The listener interface is implemented by a handler instance."
"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.