User Tools

Site Tools


Sidebar

Jump to
AmbientTalk
CRIME
iScheme

at:tutorial:modular

This is an old revision of the document!


Modular Programming

This Tutorial is still under heavy construction!

In this tutorial chapter, we introduce AmbientTalk's language features that have to do with writing modular programs. The term “modular” can be taken quite broadly in this context. By “modular” programming, we here mean programs hierarchically structured into multiple files and objects composed from multiple so-called “trait” objects. We also describe AmbientTalk's lightweight support for classifying objects and how this enables exception handling based on “types”.

Namespaces and the lobby

In AmbientTalk, programs are primarily partitioned into multiple files. Hence, a file can be regarded as the most coarse-grained module in an AmbientTalk system. Each file has its own namespace: a “global” variable is only globally visible in the file in which it has been defined. There is only one exception to this rule: definitions in the “init” file (which is by default the file at/init/init.at available in the system library) are considered global to all files.

Namespaces

In order for AmbientTalk programs to use code defined in other files, a file loading mechanism is required. AmbientTalk provides the concept of a namespace object to perform this task. A namespace object is an object which is internally connected to a directory path on the local file system (how a namespace object is tied to this path will be explained later). By accessing a namespace object's slots, files can be loaded. Consider, for example, a namespace object stored in the variable lib which is internally tied to the directory /home/ambienttalkuser/examples. Also consider that in the examples directory, there exists a file named math.at with the following contents:

object: {
  def factorial(n) {
    if: (n = 0) then:  {
      1
    } else: {
      n*factorial(n-1)
    }
  };
  def fib(n) {
    if: (n <= 1) then: {
      n
    } else: {
      fib(n-1)+fib(n-2)
    }
  };
};

This file can now be loaded by means of the lib namespace object as follows:

def mathModule := lib.math;
system.println(mathModule.factorial(5));

When the namespace object's math slot is accessed for the first time, the namespace object checks whether its encapsulated directory contains:

  • a file named math.at
  • a subdirectory named math

In this example, the file math.at exists in the directory /home/ambienttalkuser/examples. In this case, the file is loaded and the result of evaluating the code in that file is bound to the math slot. Recall that the math.at file defined one object with two methods. The above code snippet shows how these methods can now simply be used in the context of another file.

If the math slot is accessed a second time, the math.at file will not be loaded again. Rather, the math slot simply contains the return value from the loaded code. Hence, namespaces provide the desirable semantics that a file is loaded only the first time it is required. If the math.at file would not have existed, the call lib.math may still have succeeded if there would have existed a subdirectory named math in lib's directory. In that case, lib's math slot would be bound to a new namespace object encapsulating the directory /home/ambienttalkuser/examples/math.

By representing hierarchical directories as nested namespace objects, the AmbientTalk programmer can abstract over the actual files and directories of the underlying file system. For example, imagine that the designer of the math.at file later decides to change the file structure of his library by creating a directory named math and by placing the code of each function in a separate file (named factorial.at and fib.at). The code defined above would not be affected: the expression lib.math would then evaluate to a namespace object for the math subdirectory, and the expression mathModule.factorial would load the file factorial.at which returns a closure that is immediately invoked.

Obviously, this approach works both ways: if the library designer had started out organizing his project using subdirectories and multiple files, he can always restructure the library's file organization without breaking the interface to clients by replacing a subdirectory with an AmbientTalk file that evaluates to an object whose public slot names correspond to the file names of the original subdirectory.

The lobby

We have yet to explain how namespaces are initially tied to directories. One possibility is to define a root variable which would be bound to the “/” directory (i.e. the root directory of the file system). However, to load the contents of the file /home/ambienttalkuser/examples/math.at, this would require writing:

root.home.ambienttalkuser.examples.math

The downside of such as scheme is that the absolute path name of the math.at file is hardcoded in the AmbientTalk application. If we would later decide to move the file, our AmbientTalk code using that file would be broken! To avoid these problems, AmbientTalk provides the lobby object. The lobby is an object defined in the top-level scope whose slots are bound to namespace objects. The names of the slots, and the directory paths of the namespace objects bound to them are not provided in AmbientTalk directly, but rather when starting the AmbientTalk interpreter.

The interactive ambienttalk shell (iat) has a command-line parameter called the object path. This argument is a list of “name=path” entries separated by colons. For example, in order to create a namespace object called lib which is bound to /home/ambienttalkuser/examples, it suffices to start iat as follows:

iat -o lib=/home/ambienttalkuser/examples

Whenever a new actor is created by the AmbientTalk interpreter, it uses the entries in the object path to initialize the lobby object of the actor. Hence, by starting up the interpreter as shown above, executing lobby.lib in any actor returns a reference to a namespace object bound to the examples directory. This effectively removes absolute path names from AmbientTalk source code and even enables you to quickly reconfigure AmbientTalk code to use other libraries by loading the code using another object path.

AmbientTalk provides an alias for the lobby variable named /. Hence, rather than writing lobby.lib, it is also possible to write /.lib which is often more convenient, although perhaps a bit more cryptic.

The current namespace

AmbientTalk provides a slot named ~ which is always bound to the “current namespace”, the namespace in which the current file is contained. This is useful for loading files in a relative manner. For example, in the math.at file defined above, evaluating ~ would result in the lib namespace. Hence, if there would be a file named discretemath.at in the directory /home/ambienttalkuser/examples, then the math.at file could load it by writing:

def discreteMathModule := ~.discretemath;

Loading a module this way is useful because the author of math.at does not necessarily know of the lib namespace. Also, using lobby.lib introduces the implicit requirement that an object path for lib must be given. The slot ~ is always well-defined, so using it does not introduce additional dependencies.

Importing objects

Warning: importing from namespaces

Objects as traits

explain: using import as an object-composition mechanism: concepts behind traits (required/provided interface), aliasing, exclusion.

Classifying objects using stripes

explain: what are stripes? what kind of objects are they, stripe subtyping, stripe test, what default stripes exist

Exception Handling

explain: raise, try-catch and variants, first-class handlers, role of stripes, interface of an exception object

at/tutorial/modular.1177422189.txt.gz · Last modified: 2007/04/24 16:25 (external edit)