[This section (and the implementation of namespaces in GNU Smalltalk) is based on the paper Structured Symbolic Name Spaces in Smalltalk, by Augustin Mrazik.]
The Smalltalk-80 programming environment, upon which GNU Smalltalk is
historically based, supports symbolic identification of objects in one
global namespace—in the
Smalltalk system dictionary. This means
that each global variable in the system has its unique name which is
used for symbolic identification of the particular object in the source
code (e.g. in expressions or methods). The most important of these
global variables are classes defining the behavior of objects.
In development dealing with modelling of real systems, polymorphic
symbolic identification is often needed. By this, we mean that it
should be possible to use the same name for different classes or other
global variables. Selection of the proper variable binding should be
context-specific. By way of illustration, let us consider class
Statement as an example which would mean totally different things
in different domains:
An expression in the top level of a code body, possibly with special syntax available such as assignment or branching.
A customer’s trace report of recent transactions.
An assertion of a truth within a logical system.
This issue becomes inevitable if we start to work persistently, using
ObjectMemory snapshot to save after each session for later
resumption. For example, you might have the class
already in your image with the “Bank” meaning above (e.g. in the
live bank support systems we all run in our images) and you might decide
to start developing YAC [Yet Another C]. Upon starting to
write parse nodes for the compiler, you would find that
#Statement is boundk in the banking package. You could replace
it with your parse node class, and the bank’s
remain in the system as an unbound class with full functionality;
however, it could not be accessed anymore at the symbolic level in the
source code. Whether this would be a problem or not would depend on
whether any of the bank’s code refers to the class
when these references occur.
Objects which have to be identified in source code by their names are
Smalltalk, the sole instance of
SystemDictionary. Such objects may be identified simply by
writing their names as you would any variable names. The code is
compiled in the default environment, and if the variable is found in
Smalltalk, without being shadowed by a class pool or local
variables, its value is retrieved and used as the value of the
expression. In this way
Smalltalk represents the sole symbolic
namespace. In the following text the symbolic namespace, as a concept,
will be called simply environment to make the text more clear.
To support polymorphic symbolical identification several environments will be needed. The same name may exist concurrently in several environments as a key, pointing to diverse objects in each.
Symbolic navigation between these environments is needed. Before approaching the problem of the syntax and semantics to be implemented, we have to decide on structural relations to be established between environments.
Since the environment must first be symbolically identified to direct
access to its global variables, it must first itself be a global
variable in another environment.
Smalltalk is a great choice for
the root environment, from which selection of other environments and
their variables begins. From
Smalltalk some of the existing
sub-environments may be seen; from these other sub-environments may be
seen, etc. This means that environments represent nodes in a graph
where symbolic selections from one environment to another one represent
The symbolic identification should be unambiguous, although it will be polymorphic. This is why we should avoid cycles in the environment graph. Cycles in the graph could cause also other problems in the implementation, e.g. inability to use trivially recursive algorithms. Thus, in general, the environments must build a directed acyclic graph; GNU Smalltalk currently limits this to an n-ary tree, with the extra feature that environments can be used as pool dictionaries.
Let us call the partial ordering relation which occurs between environments inheritance. Sub-environments inherit from their super-environments. The feature of inheritance in the meaning of object-orientation is associated with this relation: all associations of the super-environment are valid also in its sub-environments, unless they are locally redefined in the sub-environment.
A super-environment includes all its sub-enviroments as
Associations under their names. The sub-environment includes its
super-environment under the symbol
#Super. Most environments
Smalltalk, the standard root environment, but they
are not required to do so; this is similar to how most classes derive
Object, yet one can derive a class directly from
Since they all inherit
Smalltalk’s global variables, it is not
necessary to define
Smalltalk as pointing to
Smalltalk in each environment.
The inheritance links to the super-environments are used in the lookup
for a potentially inherited global variable. This includes lookups by a
compiler searching for a variable binding and lookups via methods such
Global objects of an environment, be they local or inherited, may be referenced by their symbol variable names used in the source code, e.g.
#John -> aMan association exists in the particular environment or
one of its super-environments, all along the way to the root environment.
If an object must be referenced from another environment (i.e. which
is not one of its sub-environments) it has to be referenced either
relatively to the position of the current environment, using the
Super symbol, or absolutely, using the “full pathname”
of the object, navigating from the tree root (usually
through the tree of sub-environments.
For the identification of global objects in another environment, we use a “pathname” of symbols. The symbols are separated by periods; the “look” to appear is that of
As is custom in Smalltalk, we are reminded by capitalization that we
are accessing global objects. Another syntax returns the variable
Association for a particular global. The first
example above is equivalently:
The latter syntax, a variable binding, is also valid inside literal arrays.
A superclass of
defined, and many of the features of the Smalltalk-80
SystemDictionary will be hosted by that class.
RootNamespace are in turn subclasses of
To handle inheritance, the following methods have to be defined or redefined in Namespace (not in RootNamespace):
Inheritance must be implemented. When
Namespace, trying to read
a variable, finds an association in its own dictionary or a
super-environment dictionary, it uses that; for
writes and when a new association must be created,
creates it in its own dictionary. There are special methods like
#set:to: for cases in which you want to modify a binding in a
super-environment if that is the relevant variable’s binding.
This should return all the objects in the namespace, including those which are inherited.
AbstractNamespace will also implement a new set of
methods that allow one to navigate through the namespace hierarchy;
these parallel those found in
Behavior for the class hierarchy.
The most important task of the
Namespace class is to provide
organization for the most important global objects in the Smalltalk
system—for the classes. This importance becomes even more crucial in
a structure of multiple environments intended to change the semantics of
code compiled for those classes.
In Smalltalk the classes have the instance variable
holds the name of the class. Each defined class is included in
Smalltalk, or another environment, under this name. In a
framework with several environments the class should know the
environment in which it has been created and compiled. This is a new
Class which must be defined and properly used in
relevant methods. In the mother environment the class shall be included
under its name.
Any class, as with any other object, may be included concurrently in several environments, even under different symbols in the same or in diverse environments. We can consider these “alias names” of the particular class or other value. A class may be referenced under the other names or in other environments than its mother environment, e.g. for the purpose of instance creation or messages to the class, but it should not compile code in these environments, even if this compilation is requested from another environment. If the syntax is not correct in the mother environment, a compilation error occurs. This follows from the existence of class “mother environments”, as a class is responsible for compiling its own methods.
An important issue is also the name of the class answered by the class for the purpose of its identification in diverse tools (e.g. in a browser). This must be changed to reflect the environment in which it is shown, i.e. the method ‘nameIn: environment’ must be implemented and used in proper places.
Other changes must be made to the Smalltalk system to achieve the full
functionality of structured environments. In particular, changes have
to be made to the behavior classes, the user interface, the compiler,
and a few classes supporting persistance. One small detail of note is
that evaluation in the REPL or ‘Workspace’, implemented
by compiling methods on
UndefinedObject, make more sense if
UndefinedObject’s environment is the “current environment” as
Namespace current, even though its mother
environment by any other sensibility is
Using namespaces is often merely a matter of adding a ‘namespace’
option to the GNU Smalltalk XML package description used by
PackageLoader, or wrapping your code like this:
Namespace current: NewNS [ … ]
Namespaces can be imported into classes like this:
Stream subclass: EncodedStream [ <import: Encoders> ]
Alternatively, paths to
classes (and other objects) in the namespaces will have to be specified
completely. Importing a namespace into a class is similar to C++’s
using namespace declaration within the class proper’s definition.
Finally, be careful when working with fundamental system classes. Although you can use code like
Namespace current: NewNS [ Smalltalk.Set subclass: Set [ <category: 'My application-Extensions'> … ] ]
this approach won’t work
when applied to core classes. For example, you might be successful with
WriteStream object, but subclassing
SmallInteger this way can bite you in strange ways: integer
literals will still belong to the
Smalltalk dictionary’s version
of the class (this holds for
Strings, etc. too),
primitive operations will still answer standard Smalltalk
SmallIntegers, and so on. Similarly,
word-shaped will recognize 32-bit
LargeIntegers belonging to your own namespace.
Unfortunately, this problem is not easy to solve since Smalltalk has to
know the OOPs of determinate class objects for speed—it
would not be feasible to lookup the environment to which sender of a
message belongs every time the
+ message was sent to an Integer.
So, GNU Smalltalk namespaces cannot yet solve 100% of the problem of clashes between extensions to a class—for that you’ll still have to rely on prefixes to method names. But they do solve the problem of clashes between class names, or between class names and pool dictionary names.
Namespaces are unrelated from packages; loading a package does not import the corresponding namespace.