Names and Environments

Symbols and Namespaces

Symbols represent identifiers, and do not need much functionality. Scheme needs to be able to convert them to and from Scheme strings, and they need to be interned (which means that there is a global table to ensure that there is a unique symbol for a given identifier). Symbols are immutable and have no accessible internal structure.

Originally, Scheme symbols were represented using interned Java Strings, but they are now represented using a Symbol class: A Symbol is stateless: Common Lisp-style value, function and property list bindings are not part of the Symbolclassname> itself, but looked up in the current Environment.

class Symbol
  protected String name;
  Namespace namespace;

A Symbol has two components: The name is its printable (local) name. In an uninterned Symbol the namespace is null. But normally a Symbol is interned in a Namespace, which is a mapping from printable name to Symbol objects (whose name field is the printable name and whose namespace points back to that Namespace.) A Namespace is similar to a Common Lisp package.

class Namespace
  protected String name;
  public static final Namespace EmptyNamespace;
  public Symbol lookup(String key) { ... }

Most commonly the namespace of a Symbol is Namespace.EmptyNamespace, whose name is the empty string "".


class Environment
{ ...;

An Environment is a mapping from symbols to bindings, which are locations that can hold a value. It is used for the bindings of the user top-level. There can be multiple top-level Environments, and an Environment can be defined as an extension of an existing Environment. The latter feature is used to implement the various standard environment arguments that can be passed to eval. Nested environments were also implemented to support threads, and fluid bindings (even in the presence of threads).

An Environment is actually a 2-dimensional mapping from a pair of a Symbol and an arbitrary property object to locations. A normal value-lookup is done using a null property. In a language like Emacs Lisp or Common Lisp, which has a separate namespace for functions, you'd get the function binding of a Symbol by doing a lookup using Symbol and specifying the constant EnvironmentKey.FUNCTION as the property.

Each Kawa language defines an Environment of pre-defined definitions. (This Environment is immutable once it's been initialized.) This is the value of the getLangEnvironment() method of the Language object. There may be multiple Language objects in use, but the current context language is that returned by Language.getDefaultLanguage(). There is a special magic BuiltinEnvironment which forwards all lookups to Language.getDefaultLanguage().getLangEnvironment(). In addition, the context has a user Environment. By default, this is per-thread (and its name is set from the thread-name). If the thread is a RunnableClosure then the thread's Environment will inherit from the parent (originating) thread's Environment; otherwise the user environment inherits from BuiltinEnvironment (at least by default).

Thus a name lookup will first search the user environment, then that of its parent threads (if it was created as a RunnableClosure), and then the BuiltinEnvironment - i.e. the language builtins. Thus you can switch the current language but still have access to the user definitions.