Traditionally, Smalltalk systems were built in the context of an object memory image. Smalltalk class definitions loaded from a source file were interpreted in the context of the image. While an image-based development environment contributes significantly to the agility of Smalltalk's integrated suite of tools, it introduces some difficulties for code, component and system configuration management in large development projects.
A declarative, file based programming model makes it much easier to use existing commercial version control and configuration management tools. The Java language is file based, both for its source code (.java) and its executable binaries (.class). The Java platform also supports the deployment of class libraries as files in archival form (as .zip, .jar, etc.). In order to leverage these features of the Java platform, Bistro utilizes a declarative language model for its source code files (.bist). The current Bistro compiler performs source-to-source translation, from Bistro to Java. Then, any commercial Java compiler can be used to compile the intermediate Java source code into Java class files.
The overall source code structure of each Bistro class resembles that found in Java, including the location of classes with respect to their packages. However, the specific syntax used to declare a Bistro class resembles that found in Smalltalk. The following code example provides a general template for Bistro class definitions.
"Identify the package for the class."
"Make all the Collections visible."
"Define a new class and metaclass."
Superclass subclass: Subclass
metaclass: [ "..." ]
class: [ "..." ]
The above example shows a template for only one of the several possible kinds of Bistro class and type definitions. Table 2 provides a more complete set of examples of the possible Bistro class and type definition templates.
|Table 2. Definition Templates|
Notice that root classes and interfaces are derived from nil.
Root types have no equivalent Java supertype, but root classes are
java.lang.Object and root metaclasses are derived from
smalltalk.behavior.Class. Thus, the
smalltalk.behavior.Object class is derived from
java.lang.Object and its metaclass is derived from
See the discussion of metaclasses below
for more details.
The absence of a name space mechanism is one of the major deficiencies of most commercial Smalltalk environments. The absence of name spaces permits the occurrence of class naming conflicts, especially when integrating large class libraries from independent software development organizations, e.g., third party vendors. While some name space models have been proposed for Smalltalk1, 2, 3 none has yet been widely adopted by Smalltalk vendors, and the ANSI X3J20 committee did not include any name space model in the Smalltalk standard4.
Luckily, the Java language model supports the name space concept with packages. Packages organize Java classes into logical partitions that serve as name spaces for classes. This helps system designers prevent potential class naming conflicts. Java class files are physically organized into package directories in a file system. There is a direct correspondence between these logical and physical organizations. Figure 1 depicts these relationships. Because Bistro code is translated into Java class files, Bistro reuses the Java package mechanism and the advantages it provides.
Figure 1. Physical vs. Logical Packaging
All classes defined in a package are immediately visible to each other. Public classes from other packages can be made visible by importing them. An import establishes visibility to an individual class or all the public classes contained in a package.
As with Java, any class outside the scope of the current package
may be imported, or a class may be qualified by the name of the package in
which it was defined. So, while the import in the above code fragment makes
OrderedCollection visible, it may also be fully qualified as
Bistro classes are translated into Java classes. Bistro class member variables and methods become Java class variables and methods. However, Smalltalk has first-class metaclasses, while Java does not. Bistro implements its metaclasses by splitting each class definition in two parts - one Java class for the Bistro class methods and variables, and one Java class for the Bistro metaclass methods and variables. Putting Bistro metaclass methods and variables in another Java class allows the metaclass hierarchy to parallel the class hierarchy. This provides inheritance and polymorphism for metaclasses like that found in Smalltalk.
Each Bistro metaclass is implemented as a public static inner class of a Java class. The inner class metaclass name is always mClass. Then, each class has a public static member ($class) that refers to the sole instance of its singleton metaclass. However, because metaclasses are singletons, they do not support the definition of abstract methods. The following list shows the parallels between a few selected classes and their metaclasses.
Figure 2 shows the full inheritance hierarchy for
some of the essential behavior classes. Note each class has a static
link to its metaclass ($class). However, these links must be resolved (by
instantiation) after the inheritance links have been established (during
compilation). Making each metaclass a public static inner class enables
this. Finally, the metaclass ($class) links of all metaclasses refer a
singleton public static instance of the class
Metaclass. Figure 2 depicts
these relationships for the core behavior classes. All root classes, those
derived from nil, have inheritance and metaclass structures like that of
smalltalk.behavior.Object. A Bistro class can also be derived from a Java class. In this
case, the inheritance and metaclass structures of the generated classes also
looks like that of
Figure 2. Classes and Metaclasses
Support for object interfaces is one of the more powerful and innovative features of Java. Interfaces provide a language mechanism for defining polymorphic behavior independent of inheritance. Bistro supports the definition and use of interfaces as types. However, because Smalltalk supports first-class metaclasses and full metaprogramming, Bistro takes a further step by coupling each type with a metatype. This gives software designers the ability to coordinate programming and metaprogramming.
Each Bistro type and metatype is translated into a Java interface definition. Each metatype interface is defined as an inner class of its corresponding type interface, similar to the relationship between a class and its metaclass. As with Java interfaces, Bistro supports type inheritance (see Table 2). As with classes and metaclasses, the metatype inheritance structures parallel the type inheritance structures. When a Bistro class implements a type (see Table 2), the metaclass also implements the metatype. Also, when a Bistro class implements a Java interface, a metatype does not exist. So, the metaclass does not implement a metatype.
Access controls play an important role in defining contracts in object-oriented designs. Like Java, Bistro provides access controls on classes, types (interfaces), methods and variables. Bistro supports the Java reserved words that specify access control, including public, protected, private. While each class, method and variable may be declared with any of these three access controls, Bistro uses the common Smalltalk conventions for default access when no explicit control has been supplied. By default, Bistro classes and types are public, methods are public, and variables are protected. Also, Bistro metaclasses and metatypes are always public. All access controls are enforced at runtime by the Java VM.
Like Java, Bistro supports the use of several reserved words in variable and method signatures, including those that control subclass derivation, and thread synchronization. The following code fragments illustrate these features.
"Define a static constant."
protected static final Zero := 0.
"Define a private synchronized method."
private synchronized syncMethod: argument [ "..." ]
"Define a protected final method."
protected final finalMethod: argument [ "..." ]
"Define a public abstract method."
abstract abstractMethod 
"Define a public native method."
native nativeMethod: argument 
"Define a public static main method."
static (void) main: arguments (String) [ "..." ]
Notice that both abstract and native method declarations must have empty blocks because their implementations cannot be specified. An abstract method must be implemented in the subclasses of the class in which it is defined. A final method cannot be implemented (overridden) in subclasses. A native method must be implemented using a Java Native Interface (JNI). A static method is not polymorphic like the methods defined in a metaclass. However, a static method can be used to define the main entry point for a console application.
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.