Product Documentation

ActiveTcl 8.4 Documentation


XOTcl - Tutorial - Index

Version: 1.5.5

Introduction

 
Language Overview

XOTcl [Neumann and Zdun 2000a] is an extension to the object-oriented scripting language OTcl [Wetherall and Lindblad 1995] which itself extends Tcl [Ousterhout 1990] (Tool Command Language) with object-orientation. XOTcl is a value-added replacement for OTcl and does not require OTcl to compile.

XOTcl runs in the tclsh and provides a few extension commands. These are offered in a Tcl namespace ::xotcl, and can be imported into the current namespace to reduce typing and improve readability. All Tcl commands remain available (and are also applicable on the extension constructs).

A central property of Tcl is, that it uses strings solely for the representation of data. Internally it uses an dynamic type system with automatic conversion (which enables efficient type handling). For that reason all components (e.g. written in C) once integrated in Tcl automatically fit together and the components can be reused in unpredicted situations without change. The evolving component frameworks provide a high degree of code reuse, rapid application development, and ease of use. The application developer may concentrate on the application task solely, rather than investing efforts in fitting components together. Therefore, in certain applications scripting languages like Tcl are very useful for a fast and high-quality development of software (see [Ousterhout 1998] for more details).

Tcl is equipped with appropriate functionalities for the easy gluing of components, like dynamic typing, dynamic extensibility, and read/write introspection. OTcl is an object-oriented extension to Tcl, which encourages a Tcl-like programming style and is composed of language constructs with properties similar to Tcl. It offers an object-orientation with encapsulation of data and operation without protection mechanisms and single and multiple inheritance. Furthermore it enables to change the relationships dynamically, offers read/write introspection, has a three level class system based on meta-classes and offers method chaining. These abilities are integrated in XOTcl with only slight changes to OTcl visible to the programmer.

The XOTcl extension aims at complexity and adaptability issues that may occur in context of large (object-oriented) software structures and in the context of component glueing. In particular we added the following support:

  • Filters as a means of abstractions over method invocations to implement large program structures, like design patterns.

  • Mixin Classes, as a means to give an object or a classes' instances access to several different supplemental classes, which may be changed dynamically.

  • Dynamic Object Aggregations, to provide dynamic aggregations through nested namespaces.

  • Nested Classes, to reduce the interference of independently developed program structures.

  • Assertions, to reduce the interface and the reliability problems caused by dynamic typing and, therefore, to ease the combination of components.

  • Forwarders, to delegate calls efficiently to other objects or classes.

  • Slots, to manage values of instance variables with a common interface.

  • Meta-data and Automatic Documentation, to enhance self-documentation of objects and classes.


  

Figure 1: Language Extensions of XOTcl

 
Introductory Overview Example: Soccer Club

To give you an impression of the language before we go into the details of the language construct, we present in this section a simple, introductory example. It shall demonstrate the basic language constructs on the example of a soccer club (the full code can be found in the xotcl/src/scripts/soccerClub.xotcl file. All the characters in this example are fictitious, and any resemblance to actual persons, living or deceased, is coincidental.

In XOTcl we do not have to provide a file description as a comment, but we can use the @ object, which is used generally to provide any kind of information, meta-data, and documentation on a running program. Here, we just give a file description. Then the makeDoc.xotcl tool can automatically document the program file for us.

  @ @File {
    description {
      This is a simple introductory example for the language XOTcl. 
      It demonstrates the basic language constructs on the example of
      a soccer club.
    }
  }

All things and entities in XOTcl are objects, a special kind of objects are classes. These define common properties for other objects. For a soccer club, we firstly require a common class for all kinds of members.

Common to all members is that they have a name. Common properties defined across all instances of a class are called 'parameter' in XOTcl. In this example the instance variable name will be initialized by default with an empty string.

  Class ClubMember -parameter {{name ""}}

A special club member is a Player. Derived classes can be build with inheritance (specified through superclass). Players may have a playerRole (defaults to NONE).

  Class Player -superclass ClubMember -parameter {{playerRole NONE}}

Other club member types are trainers, player-trainers, and presidents:

  Class Trainer -superclass ClubMember
  Class President -superclass ClubMember

The PlayerTrainer uses multiple inheritances by being both a player and a trainer:

  Class PlayerTrainer -superclass {Player Trainer}

Now we define the SoccerTeam class:

  Class SoccerTeam -parameter {name location type}

We may add a player. This is done by a method. Instance methods are in XOTcl defined with instproc. All club members are aggregated in the team (denoted by :: namespace syntax).

  SoccerTeam instproc newPlayer args {
    # we create a new player who is part of the soccer team
    # "eval" is needed to pass the provided arguments to the call of new
    eval Player new -childof [self] $args
  }

A player can be transfered to another team. The player object does not change internally (e.g. the playerRole stays the same). Therefore we move it to the destination team.

  SoccerTeam instproc transferPlayer {playername destinationTeam} {
    # We use the aggregation introspection option children in order
    # to get all club members
    foreach player [my info children] {
      # But we only remove matching playernames of type "Player". We do
      # not want to remove another club member type who has the same
      # name.
      if {[$player istype Player] && [$player name] == $playername} {
        # We simply 'move' the player object to the destination team.
        # Again we use a unique autoname in the new scope
        $player move [set destinationTeam]::[$destinationTeam autoname player%02d]
      }
    }
  }

Finally we define two convenience to print the members/players to the stdout with puts.

  SoccerTeam instproc printMembers {} {
    puts "Members of [my name]:"
    foreach m [my info children] {puts "  [$m name]"}
  }
  SoccerTeam instproc printPlayers {} {
    puts "Players of [my name]:"
    foreach m [my info children] {
      if {[$m istype Player]} {puts "  [$m name]"}
    }
  }

Now let us build to example soccer team objects.

  SoccerTeam chelsea -name "Chelsea FC" -location "Chelsea"
  SoccerTeam bayernMunich -name "F.C. Bayern München" -location "Munich"

With addPlayer we can create new aggregated player objects

Let us start some years in the past, when "Franz Beckenbauer" was still a player.

  set fb [bayernMunich newPlayer -name "Franz Beckenbauer" \
    -playerRole PLAYER]

playerRole may not take any value. It may either be NONE, PLAYER, or GOALY ... such rules may be given as assertions (here: an instinvar gives an invariant covering all instances of a class). In XOTcl the rules are syntactically identical to if statements:

  Player instinvar {
      {[my playerRole] == "NONE" ||  
       [my playerRole] == "PLAYER" || 
       [my playerRole] == "GOALY"}
  }

If we break the invariant and turn assertions checking on, we should get an error message:

  $fb check all
  if {[catch {$fb set playerRole SINGER} errMsg]} {
    puts "CAUGHT EXCEPTION: playerRole has either to be NONE, PLAYER, or TRAINER"
    # turn assertion checking off again and reset to PLAYER
    $fb check {}
    $fb set playerRole PLAYER
  }

But soccer players may play quite different, orthogonal roles. E.g. Franz Beckenbauer was also a singer (a remarkably bad one). However, we can not simply add such orthogonal, extrinsic extensions with multiple inheritance or delegation. Otherwise we would have either to build a lot of unnecessary helper classes, like PlayerSinger, PlayerTrainerSinger, etc., or we would have to build such helper objects. This either leads to an unwanted combinatorial explosion of class or object number

Here we can use a per-object mixin, which is a language construct that expresses that a class is used as a role or as an extrinsic extension to an object.

First we just define the Singer class.

  Class Singer
  Singer instproc sing text {
    puts "[my name] sings: $text, lala."
  }

Now we register this class as a per-object mixin on the player object:

  $fb mixin Singer

And now Franz Beckenbauer is able to sing:

  $fb sing "lali"

But Franz Beckenbauer has already retired. When a player retires, we have an intrinsic change of the classification. He *is* not a player anymore. But still he has the same name, is club member, and is a singer (brrrrrr).

Before we perform the class change, we extend the Player class to support it. I.e. the playerRole is not valid after class change anymore (we unset the instance variable).

  Player instproc class args {
    my unset playerRole
    next
  }

Now we can re-class the player object to its new class (now Franz Beckenbauer is President of Bayern Munich.

  $fb class President
  # Check that the playerRole isn't there anymore.
  if {[catch {$fb set playerRole} errMsg]} {
    puts "CAUGHT EXCEPTION: The player role doesn't exist anymore \
         (as it should be after the class change)"
  }

But still Franz Beckenbauer can entertain us with what he believes is singing:

  $fb sing "lali"

Now we define some new players for Bayern Munich:

  bayernMunich newPlayer -name "Oliver Kahn" -playerRole GOALY
  bayernMunich newPlayer -name "Giovanne Elber" -playerRole PLAYER

If we enlist the players of Munich Franz Beckenbauer is not enlisted anymore:

  bayernMunich printPlayers

But as a president he still appears in the list of members:

  bayernMunich printMembers

Now consider an orthonogal extension of a transfer list. Every transfer in the system should be notified. But since the transfer list is orthogonal to SoccerTeams we do not want to interfere with the existing implementation at all. Moreover, the targeted kind of extension has also to work on all subclasses of SoccerTeam. Firstly, we just create the extension as an ordinary class:

  Class TransferObserver
  TransferObserver instproc transferPlayer {pname destinationTeam} {
    puts "Player '$pname' is transfered to Team '[$destinationTeam name]'"
    next
  }

Now we can apply the class as a per-class mixin, which functions exactly like a per-object mixin, but on all instances of a class and its subclasses. The next primitive ensures that the original method on SoccerTeam is called after notifying the transfer (with puts to stdout):

  SoccerTeam instmixin TransferObserver

If we perform a transfer of one of the players, he is moved to the new club and the transfer is reported to the stdout:

  bayernMunich transferPlayer "Giovanne Elber" chelsea

Finally we verify the transfer by printing the players:

  chelsea printPlayers
  bayernMunich printPlayers



Object and Class System

In XOTcl every object is associated with a class over the class relationship. Classes are special objects with the purpose of managing other objects. ``Managing'' means that a class controls the creation and destruction of its instances and that it contains a repository of methods (``instprocs'') accessible for the instances. Object-specific methods are called ``procs'', instance methods are called ``instprocs''.

The instance methods common to all objects are defined in the root class Object (predefined or user-defined). Since a class is a special (managing) kind of object it is managed itself by a special class called ``meta-class'' (which manages itself). Meta-Classes are used to define classes and to provides methods for these. Most classes are defined by the predefined meta-class Class. One interesting aspect of meta-classes is that by providing a constructor pre-configured classes can be derived. Meta-classes can be used to instantiate large program structures, like some design patterns (see [Neumann and Zdun 1999a] for more details), where the meta-class may holds the generic parts of the structures. Since a meta-class is an entity of the program, it is possible to collect these entities in pattern libraries for later reuse easily (more details about meta-classes are given in a later section).

XOTcl supports single and multiple inheritance. Classes are ordered by the relationship superclass in a directed acyclic graph. The root of the class hierarchy is the class Object. A single object can be instantiated directly from this class. An inherent problem of multiple inheritance is the problem of name resolution, when for example two super-classes contain an instance method with the same name. XOTcl provides an intuitive and unambiguous approach for name resolution by defining the precedence order along a linear ``next-path'' incorporating the class and mixin hierarchies, which is modeled after CLOS. A method can invoke explicitly the shadowed methods by the predefined command next. When this command is executed a shadowed method is ``mixed into'' the execution of the current method. Method chaining without explicit naming of the targeted method is very important for languages supporting a dynamic class system, because one cannot always predict which classes are currently participating in the inheritance hierarchy at design time (often necessary in inheritance models, like C++).

An important feature of all XOTcl objects is the read/write introspection. The reading introspection abilities of XOTcl are packed compactly into the info instance method which is available for objects and classes. All obtained information can be changed at run-time dynamically with immediate effect. Unlike languages with a static class concept, XOTcl supports dynamic class/superclass relationships. At any time the class graph may be changed entirely using the superclass method, or an object may change its class through the class method. This feature can be used for an implementation of a life-cycle or other intrinsic changes of object properties (in contrast to extrinsic properties e.g. modeled through roles and implemented through per-object and per-class mixins [Neumann and Zdun 1999c] ) . These changes can be achieved without loosing the object's identity, its inner state, and its per-object behavior (procs and per-object mixins).

  

Figure 2: Object and Class System

Basic Functionalities

 
Objects

Initially XOTcl offers two new commands: Object and Class. They represent hooks to the features of the language. This section discusses both of them in detail and shows how they function in the context of XOTcl. Note, that even if most of this is compatible to OTcl, a few changes occur. For this reason, this section is no introduction to plain OTcl. The Object command provides access to the Object class, which holds the common features of all objects, and allows us to define new objects. Objects are always instances of classes, therefore, objects defined with the Object command are (initially) instances of the Object class. But since they have no user-defined type, they may be referred to as singular objects. As all other objects they may be specialized by object-operations and -data.

The object command has the following syntax:

  Object objName ?args?

A command of this form is a short-cut for a message to the create instance method (forwarded automatically by the unknown mechanism, which is invoked every time the message dispatch system discovers an unknown message):

  Object create objName ?args?

It creates a new object of type Object with the name objName (in fact it invokes a create call on the Object class). objName becomes a new command, which allows us to access the created object. Similar to the Object command it may be used like a normal Tcl-command (using sub-commands to access the object's methods). Therefore, this form of access is called object-command approach. A simple example is an object which holds the information of a kitchen. It is created by:

  Object kitchen

An object creation calls the constructor init of the object's class. The destruction of an object is handled by the destroy instance method. The general syntax of destroy is:

  objName destroy

E.g. the kitchen object is destroyed by:

  kitchen destroy

To invoke a user-defined destruction process, it is possible to overload this instance method in every class derived from object.

Note that the destruction of an object is performed by the method destroy of Object (since every object is an instance of Object, every object can call destroy). When an application class overloads destroy, this method should contain a next in order to reach the base class and to actually destroy the object.

Data on Objects

The Object class provides a range of operations to manage objects, including those to manipulate data-structures on the objects. They are similar to the same-named Tcl-commands:

  objName set varname ?value?
  objName unset v1 ?v2 ... vn?

The set instance method with given value option allows us to manipulate an object-variable's value or to create a new one, if the variable varname does not exist on the object so far. Without value option the set operation queries the variable and returns it's value, if the variable exists, otherwise it produces an error message. The unset operation deletes one or optionally a set of variables from an object. For example the kitchen object can store information on the color of the wall-paper by:

  kitchen set wallPaperColor white

Similar to Tcl-variables the object variables are dynamical; they may be set at run-time when they are needed and unset when they become obsolete. E.g. the persons in the kitchen may be stored in an array. If there are no persons in the kitchen the array is deleted:

  # Peter enters the kitchen to cook
  kitchen set persons(cook) Peter
  ...
  # Marion enters the kitchen to take one of the seats
  kitchen set persons(seat1) Marion 
  ...
  # Both Peter and Marion leave the kitchen
  # the array is deleted by unset
  kitchen unset persons

Since XOTcl variables are internally realized through Tcl-variables they may be treated like all Tcl-variables. For that reason they have all Tcl-variable abilities, including the possibility to handle them as lists or arrays (as seen in the last example). The array command of Tcl is mapped to an XOTcl-command directly. An object-oriented call to an object of the form

  objName array option arrayName args

forwards its arguments to an array Tcl-command for the object´s instance variable arrayName. It could be used like the same-named Tcl-command, e.g. the command

  kitchen array names persons

returns all indexes currently stored in the persons array.

Similarly Tcl´s incr command is mapped to the object system. A call with the syntax:

  objName incr varName ?value?

increments varName with the given value (or without given value with 1).

Methods for Objects

Methods in XOTcl resemble Tcl-procedures. On objects one can define object-specific methods, called procs. Instance methods which are defined on classes are called instprocs. A new proc is defined using the proc instance method of the class Object:

  objName proc name args body

The arguments of the proc instance method specify the name, the arguments as a Tcl-list, and the body of the new proc. All of them must be given, only one of args and body may be empty. An example proc would be a method to let persons enter the kitchen:

  kitchen proc enter {name} {
    [self] set persons($name) [clock seconds]
  }

Here the predefined self command is used in one of three possible ways, which allow us to access useful information when working with XOTcl-methods, these are in particular:

  • self: returns the name of the object, which is currently in execution. This command is similar to this in C++. It is automatically generated on each object. If it is called from outside of an XOTcl method, it produces the error message "Can't find self".

  • self class: the self command with the argument class returns the name of the class, which holds the currently executing instproc. Note, that this may be different to the class of the current object. If it is called from a proc it returns an empty string.

  • self proc: the self command with the argument proc returns the name of the currently executing method (proc or instproc).

The method enter can be written in XOTcl as well with less syntactic overhead by using the predefined primitive my instead of [self]:

  kitchen proc enter {name} {
    my set persons($name) [clock seconds]
  }

Note, that there is a difference to the realization of these object informations to OTcl. XOTcl uses commands in order to make XOTcl-methods compatible to Tcl-procedures and accessible via namespace-paths. OTcl uses the three variables self, class and proc, which are filled automatically with proper values by the interpreter each time a method is called. To gain backwards compatibility XOTcl can be compiled with -DAUTOVARS to provide these variables additionally. By default this option is turned off.

Each XOTcl-method has its own scope for definition of local variables for the executing method. In most cases when a method uses object-variables, it is likely that the programmer wants to make one or more of these variables part of the method's scope. Then the Tcl-command for variable handling, like set, lindex, array, ... work also on these variables. The instvar instance method links a variable to the scope of an executing method. It has the syntax:

  objName instvar v1 ?v2 ... vn?

It makes the variables v1 ... vn, which must be variables of the object, part of the current method's scope. A special syntax is:

  objName instvar {varName aliasName} ...

for one of the variables. This gives the variable with the name varName the alias aliasName. This way the variables can be linked to the methods scope, even if a variable with that name already exists in the scope. Now the enter method can be adapted slightly and a leave method can be added, which uses Tcl's info command to check whether the named person is in the object's persons array. To demonstrate the alias-syntax this is done with the persons array and the alias p.

  kitchen proc enter {name} {
    my instvar persons
    set persons($name) [clock seconds]
  }

  kitchen proc leave {name} {
    my instvar {persons p}
    if {[info exists p($name)]} {
      puts "$name leaves after [expr {[clock seconds]-$p($name)}] seconds" 
      unset p($name) 
    } else {
      puts "$name is not in the room"
    }
  }
A method defined via proc can be deleted by proc using an empty argument list and an empty body. The following example deletes the method enter:
  Room proc enter {} {}

Information about Objects

XOTcl offers reading and writing introspection. The reading introspection abilities are packed compactly into the info instance method which is available for objects and classes (there are special info options for object aggregations, nested classes, mixins, filters, meta-data and assertions, which are explained separately in the following sections).

Options for the info method on objects

objName info args methodName

Returns the arguments of the specified proc (object specific method).

objName info body methodName

Returns the body of the specified proc.

objName info class ?className?

Returns the name of the class of the current object, if className was not specified. Otherwise it returns 1 if className matches the object's class and 0 if not.

objName info commands ?pattern?

Returns all commands defined on the object if pattern was not specified. Otherwise it returns all commands that match the pattern.

objName info default methodName arg var

Returns 1 if the argument arg of the specified proc has a default value, otherwise 0. If the default value exists it is stored in var.

objName info precedence ?pattern?

Returns all classes in the precedence order from which the specified object inherits methods. The returned list of classes contains the mixin and instmixin classes as well as the classes of the superclass chain in linearized order (i.e., duplicate classes are removed). If the pattern is specified, only matching classes are returned.

objName info vars ?pattern?

Returns all variables defined on the object if pattern was not specified, otherwise it returns all variables that match the pattern.


For example on the kitchen object

  kitchen info procs

returns enter and leave as a Tcl-list since these are the procs defined on the object.

Classes

Creating Classes and deriving Instances

There are different ways to create a class in XOTcl. They have in common that they derive the new class from a meta-class. Initially the Class command provides access to the meta-class Class, which holds the features common to all classes. It also allows one to derive new meta-classes. The common way to create a new class is:

  Class className ?args?

Similar to the object short form, this is a short form of a call to the create instance method of the meta-class Class, which is also executed by the standard unknown mechanism. This mechanism is always triggered when XOTcl does not know a method called on an object. Supposed that there is no method with the name className, defined on the class-object of Class, XOTcl looks up the method unknown (which is found on the Class Object) and executes it. The standard unknown-mechanism of XOTcl calls create with all arguments stepping one step to the right; in the general case:

  Class create className ?args?

This may also be called directly. Besides the indirection when using unknown, in most cases there is no difference in the action performed: Firstly the memory is allocated, using the alloc instance method; as the next step the constructor init is called on the creating object, which is in this case the class-object of the meta-class Class. In seldom cases the programmer may want to suppress the init call. To do so the alloc instance method may also be called directly:

  Class alloc className ?args?

As seen in the preceding section objects are created in the same way. The difference was, that the command Object, which accesses a class, instead of the command Class, which accesses a meta-class, was used. The user-defined classes may also be used in the same way to create new objects:

  className objName ?args?

Resembling the creation of classes this creates an object objName of type className using the unknown mechanism. That means the create instance method of the class is called. If there is no other instance method defined on the class-path so far (which would mean, an user defined creation process is invoked), the create instance method of the class Object is invoked. This method is similar to the create method of the meta-class Class. It firstly calls the alloc instance method on its (of the Class class) which allocates memory for the object, and makes it an instance of it's class. Afterwards a call to the constructor init is invoked.

Now we can specify the object for the kitchen by the class to which it belongs. In this case a kitchen is an instance of a room.

  Class Room
  Room kitchen

A set call on a class creates an instance variable on the class-object. This variable is unique for all instances, therefore, it may be referred to as a class variable.

Methods Defined in Classes

Methods which are defined in classes and which are provided to the instances of these classes are called "instprocs". The syntax for defining an instproc is:

  className instproc procname args body

It is similar to the definition of procs on objects, but uses the keyword instproc to distinguish between the methods defined on the class-object and those defined on the class. Since all rooms (in the modeled world) have ceilings, we may want to define a simple convenience instproc, which is able to set the color:

  Room instproc setCeilingColor color {
    my set ceilingColor $color
  }

A special instproc, the constructor init, was mentioned already. Now we are able to define such an instproc. Defined on a class it is responsible for all initialization tasks, which needed to be performed, when constructing a new instance object of the class. The constructor of the Room can initialize a variable for the color, in which the ceiling is painted, to white as default, since this is the color of ceilings without painting.

  Room instproc init args {
    my setCeilingColor white
    next
  }

After this definition, all instances derived from the Room class have an instance variable ceilingColor with the value white. The args argument used here is a special argument in Tcl which allows us to use a list of arguments which may change its length from call to call.

An instproc can be deleted by the method instproc as well. If instproc is called with an empty argument list and an empty body, the specified method is deleted, as the following example shows:

  Room instproc setCeilingColor {} {}

Information about Classes

Resembling to objects, information on classes may be gained through the info instance method of the meta-class Class. Note that this instance method does not only support the class info options, but also the object info options, since the accessing command refers to the class-object, which itself is an object and, therefore, offers its informations. The following table summarizes the additional info options available on classes.

Options for the info method on classes

className info heritage ?pattern?

Returns a list of all classes in the precedence order of the class hierarchy matching pattern or a list of all classes, if pattern was not specified.

className info instances ?pattern?

Returns a list of the instances of the class matching pattern or of all instances, if pattern was not specified.

className info instargs methodName

Returns the arguments of the specified instproc (method provided to objects).

className info instbody methodName

Returns the body of the specified instproc.

className info instcommands ?pattern?

Returns all commands defined on the class, if pattern was not specified, otherwise it returns all commands provided to objects that match the pattern.

className info instdefault methodName arg var

Returns 1 if the argument arg of the specified instproc has a default value, otherwise 0. If the default value exists it is stored in var.

className info subclass ?className2?

Returns a list of all subclasses of the class, if className2 was not specified, otherwise it returns 1 if className2 is a subclass and 0 if not.

className info superclass ?className2?

Returns a list of all super-classes of the class, if className2 was not specified, otherwise it returns 1 if className2 is a superclass and 0 if not.

The full list of info options is provided in the language reference.

Inheritance

Besides encapsulation of operations and state in objects, a second central ability of object-orientation is inheritance. XOTcl supports single and multiple inheritance with a directed acyclic class graph. Automatically each new class created by the instance methods create and alloc of Class inherits from Object. Therefore, it is ensured that all instances of the new class have access to the common features of objects stored in the class Object.

To specify further inheritance relationships the instance methods superclass of Class is used:

  className -superclass classList

E.g. in the example a kitchen may be seen as a special room:

  Class Room
  Class Kitchen -superclass Room

Now all instances of Kitchen are able to access the operations stored in the Room and in the Kitchen class. Note the transition the kitchen was going through: firstly it was a singular object, then it was an object with a user-defined class, and now it is a class. This is possible (and not senseless) because of the per-object specialization ability and the dual shape of a class, which is at the same time object and class. Both lead to a seamless connection of the run-time properties (the object features) and their descriptive properties (the class features). It is possible to avoid the strict distinction between them, known from static typed languages, like C++, Java, etc.

Moreover, since the syntaxes of constructs expressing the same concern are nearly identical, we can re-factor a solution with very few changes to the alternative. We will see similar "ease of refactoring" throughout the XOTcl language. E.g., we can also easily re-factor the class hierarchies or exchange class hierarchies against mixin solutions with only slight changes in the code.

Besides single inheritance, as seen, XOTcl provides also multiple inheritance. This is syntactically solved by giving the superclass instance method a list of classes instead of a single class as argument.

  Class Room
  Class 4WallsRoom -superclass Room
  Class CookingPlace
  Class Kitchen -superclass {4WallsRoom CookingPlace}

Now the kitchen class is specialized a bit more. It is a special room which has four walls and it is a cooking place. Multiple inheritance, as seen here, is as simple to apply as single inheritance.

Most often when the disadvantages of multiple inheritance are discussed, the name resolution along the class graph is considered as the biggest problem. The question is, which method is to be chosen and which path through class graph is to be taken, if more then one method of the specified name exist on the class graph.

In the example such questions would arise for an object of the Kitchen class, if two same-named methods are defined on CookingPlace and 4WallsRoom or if a method of the class Object is called, which is reachable through two paths (along CookingPlace or Room).

Often - e.g. in the inheritance model of C++ - the path through the graph is not clearly determined and/or the rules are too complicated to be understood on the first glance. The programmer often can only determine by trial which method is found firstly. Than an explicit naming of the class is necessary, which means storage of non-local information in sub-classes. Often different compilers of one language behave differently. All these issues make code reuse difficult. Moreover understandability and portability are reduced.


Figure 3: The example classes and the following next-path


XOTcl goes an intuitive and unambiguous way to solve this problem. It resolutes the precedence order along a ``next-path''. Firstly the class of the object is searched, which is Kitchen in example. Then the super-classes are searched in definition order, which means at first 4WallsRoom, then CookingPlace. Each branch is searched completely, before changing to the next branch. That means, Room is searched, before the CookingPlace branch is visited. At last the top of the hierarchy, the class Object, is searched.

The usage of next in XOTcl is different to OTcl: In OTcl, next is defined as a method, in XOTcl it is a primitive command. Furthermore, in OTcl, it is always necessary to provide the full argument list for every invocation explicitly. In XOTcl, a call of next without arguments can be used to call the shadowed methods with the same arguments (which is the most common case). When arguments should be changed for the shadowed methods, they must be provided explicitly in XOTcl as well. In the rare case that the shadowed method should receive no argument, the flag --noArgs must be used.

Destruction of Classes

Classes are destroyed by the destruction of the class-object using the destroy method of the Object class. The destruction of super-classes does not destroy the sub-classes. The super-class is simply removed from the sub-classes' super-class lists. All classes have the super-class Object, if no super-class is specified. Therefore, if all super-classes are destroyed or removed, the new super-class is Object, not: no super-class. The destruction of the class of an object does neither delete the object nor leave it without class. In XOTcl a deleted class leaves it's instances with the class Object.

So all empty class- and superclass-relationships are automatically reseted to Object. Note, that this are differences to OTcl, where the destruction of an class destroys all instances and an empty super-class list remains empty.

Method Chaining

A special feature of XOTcl is the method chaining without explicit naming of the ``mix-in''-method. It allows one to mix the same-named superclass methods into the current method (modeled after CLOS). The previously described next-path is the basis for this functionality. At the point marked by a call to the next primitive of XOTcl the next shadowed method on the next path is searched and, when it is found, it is mixed into the execution of the current method. When no method is found, the call of next returns an empty string, otherwise it returns the result of the called method. The syntax is:

  next ?arguments|--noArgs?

As stated earlier the usage of next in XOTcl differs from OTcl, since the next call without arguments in OTcl means per default that no arguments are passed. But most often all arguments are passed through to the shadowed methods (since these will most likely have the same signatures). When all variables should be passed through, in OTcl it is necessary for correct variable substitution to use:

  eval $self next $args

To avoid such difficulties, we made the passing of all arguments the default case; a simple

  next

performs the task of passing all arguments to the shadowed methods. These arguments are called the standard arguments. If the standard argument feature should not be used, optionally arguments can be given or the flag --noArgs could be set as sole argument, which means that the shadowed method is called with no arguments.

E.g. the following next call ignores the standard arguments and sends the arguments 1 and 2 instead:

  next 1 2

As an example all classes involved in the previous example should get a constructor instance method, which simply sets an instance variable on the object:

  Room instproc init args {
    my set roomNumber 0
    next
  }    
  4WallsRoom instproc init args {
    my set doorPosition 0
    next
  }
  CookingPlace instproc init args {
    my set stoveType electric
    next
  }
  Kitchen instproc init args {
    my set cookName -
    next
  }

After creation an object of class Kitchen gets automatically four instance variables cookName, roomNumber, doorPosition and stoveType set up with default values in this order (since this is the order of the classes in the next-path). Note, that the order is important, because one missing next call, in one of the init methods, means that succeeding init methods will not be executed. This mechanism functions equally on all kinds of instprocs, not only on constructors.

The constructors use the args argument, which allows us to give a list of variable length as arguments. To ensure reusability of our classes the constructors should use args in most cases, since they may pass through arguments for constructors further up the class hierarchy.

If a proc with the searched name exists on the object it shadows all instprocs. A next call in a proc leads to the normal next-paths search, starting with the object's class.

By the way, an observant reader might notice that the example above can be rewritten without explicit constructors, just by using parameters with default values.

  Class Room -parameter {{roomNumber 0}}
  Class 4WallsRoom -superclass Room -parameter {{doorPosition 0}}
  Class CookingPlace -parameter {{stoveType electric}}
  Class Kitchen -superclass {4WallsRoom CookingPlace} -parameter {{cookName -}}

If an instance of a Kitchen is created it will contain instance variables for doorPosition, cookName, roomNumber, and stoveType, as the following statements will show.

  Kitchen k
  puts [k info vars]

Dynamic Class and Superclass Relationships

Another property of XOTcl that distinguishes it from statically typed languages are dynamics of class relationships. The realization of the definition of super-classes as seen above with the superclass method suggests already, that it is not only available at the class definition time. In the above example its appended to the class definition with "-superclass" as a short syntax for method invocation at definition time (all other available methods can also be called with a preceding dash ("-") appended to definitions).

At any time the class graph may be changed entirely using the superclass method. Suppose the rooms and kitchens created in modeling of a house should be displayed to a screen, but it is not determined, whether the user of the system has the possibilities for graphical outputs. Two classes TextOutput and GraphicalOutput may be defined, which handle the output. Both have an instproc paint which does the painting of the virtual world on the chosen display type. The common output requirements are handled by a derived class VirtualWorldOutput which calls the paint method of the superclass using next. In statically typed languages it would need more sophisticated constructs to change the output class at run-time. E.g. a delegation to another object handling the intrinsic task of the output object would be introduced solely for the purpose of configuring the output form. With a dynamic class system we can use the superclass method to do so easily:

  Class TextOutput
  TextOutput instproc paint args {
    # do the painting ...
  }
  Class GraphicalOutput
  GraphicalOutput instproc paint args {
    # do the painting ...
  }

  # initially we use textual output
  Class VirtualWorldOutput -superclass TextOutput
  VirtualWorldOutput instproc paint args {
    # do the common computations for painting ...
    next; # and call the actual output
  }

  # user decides to change to graphical output
  VirtualWorldOutput superclass GraphicalOutput

Sometimes, such a change to new intrinsic properties should not happen for all instances of a class (or the class hierarchy), but only for one specific object. Then the usage of a dynamic super-class relationship is a too coarse-grained means. A second form of such dynamics is the changing of the relationship between object and class. This means, objects can also change their class dynamically at run-time. This feature may be used to model a life-cycle of an object, without loosing the object's identity, inner state or per-object-specializations through procs. The class instance method enables this functionality.

An example would be an agent for the virtual world. Agents may be placeholders for persons, who interactively travel the world, or programs, which act automatically. When a person decides at run-time to give a task it has performed formerly by hand to an automatic agent, the agents nature changes from interactive agent to automatic agent, but the identity and the local state (that means the parts of the task, that are already fulfilled by the person) stay the same. This is a scenario for changing class relationships, e.g.:

  Class Agent
  Class AutomaticAgent -superclass Agent
  Class InteractiveAgent -superclass Agent

  # create a new agent for a person
  InteractiveAgent agent1

  # the person does something ...
  # and decides the change to an automatic agent
  agent1 class AutomaticAgent

Meta-Classes

Meta-classes are a special kind of classes. Similar as classes are managing objects (where managing means: control the creation and destruction of instances, know what instances exist, provide methods), meta-classes are managing classes. So, meta-classes are used to define classes. In other words, every Class in XOTcl is created by a meta-class, in most cases by the meta-class named Class. New user-defined meta-classes can be defined as subclasses of the predefined meta-class Class, or by adding an instmixin class (see below) containing Class to the precedence chain of the class. By defining Object instmixin Class one can even change the object system of XOTcl in in a way such that every created Object is a meta-class.

Since the concept of a meta-class are sometimes confusing to people of a background of some other programming languages, we explain meta-classes slowly with the analogy of classes and objects.

When a class Foo is created via the command

   Class Foo
it has no private variables and no special methods. This is somewhat similar as creating an object via Object:
   Object foo
This plain object foo can be configured directly, or one can create a class that configures the object. Instead of writing
   Object foo 
   foo set x 1
   foo proc hi {} {puts "hello"}
one can use
   Class C -superclass Object
   C instproc init {} {my set x 1}
   C instproc hi {} {puts "hello"}
and create an instance and call the method.
   C c1
   c1 hi
The same holds for meta-classes and classes as well: Instead of writing
   Class Foo
   Foo set x 1
   Foo proc hi {} {puts "hello"}
the following can be used:
   Class MC -superclass Class
   MC instproc init {} {my set x 1}
   MC instproc hi {} {puts "hello"}
The instances of meta-classes are classes which can be defined the usual way:
   MC Bar
   Bar hi
   Bar b1
Now we have a class names Bar which has a class-scoped variable named x with the value of 1 (set via the constructor); the class Bar has as well a class-method named hi which prints, when called, the string "hello". The class Bar can be used to create instances of the class like b1, b2 and so on.

Note that the command Class is a predefined definition of the most general meta-class in XOTcl. Each time we are creating a class, we use this meta-class. In order to define a specialized meta-class, we can do this the traditional object-oriented way: we subclass. Therefore, in to define a specialized meta-class, we can use:

  Class myMetaClass -superclass Class

This defines a new meta-class myMetaClass, which has all the abilities of meta-classes. That means that the programmer is able to specify new class features or override old ones. Later she/he may instantiate these into new classes.

This is a very powerful language feature, since it allows one to give some classes further abilities than the others (or to restrict classes). This way large program structures, like certain design pattern parts, may be instantiated. Meta-classes hold the common abstract parts of the structures. They allow one to form libraries of such structures very easily.

Example 1: Overloading the info method of classes

As a simple example we can derive a new meta-class NoClassInfo from Class. Later we override the info method of Class. Thus the classes created with NoClassInfo, have an info option that only produces an error message. All classes created with NoClassInfo, like Agent in the example below, are not capable of accessing the class info method anymore:

  Class NoClassInfo -superclass Class
  # redefine info ability
  NoClassInfo instproc info args {
    error "No class info available"
  }
  # derive agent class from meta-class, which
  # can not access class info
  NoClassInfo Agent
Now a call like:
  Agent info superclass

triggers the error message.

Example 2: Defining Classes that Count Their Instances

Meta-classes are frequently used to define some bookkeeping about the number of instances on the class level. In the following example we define a meta-class named CountedClass which defines classes that count their instances:

  Class CountedClass -superclass Class -parameter {{counter 0}}
  CountedClass instproc create args {
    my incr counter
    next
  }
  CountedClass instproc instdestroy args {
    my incr counter -1
    next
  }
  CountedClass Dog

  Dog piffie
  Dog idefix
  puts "nr of dogs: [Dog counter]"

  piffie destroy
  puts "nr of dogs: [Dog counter]"
Note that the behavior introduced by meta-classes can be orthogonal to the behavior of the classes. One can define Dog as a specialization of Animal or defines a special kind of dog such as Poodle using the method superclass as usual.

Example 3: The Singleton Meta-Class

Finally, a small example, which is more practical. Some applications have the requirement that only one instance of a class might be defined at a certain time. Such a behavior is frequently called a "Singleton". In XOTcl we can define a class singleton by overloading the create method of Class: when create is called and there exists already an instance of the singleton it is returned instead of a new instance.

  Class Singleton -superclass Class
  Singleton instproc create args {
    expr {[my exists instance] ? [my set instance] : [my set instance [next]]}
  }
If someone wants to have a class e.g. Manager to be a singleton, you can create it by e.g.
  Singleton Manager -superclass FOO

Create, Destroy, and Recreate Methods

XOTcl allows since version 0.84 for a flexible destroy and recreate scheme. create and alloc are both Class instprocs handling creation for their instances. I.e.:

 className alloc [self]
and
 className create [self]

are used for creating an instance. A similar method instdestroy exists on Class that handles physical destruction of an object. The method destroy on Object which lets an object destroy itself in fact has the following behavior:

  Object instproc destroy args {
   [my info class] instdestroy [self]
  }

However, this behavior is not implemented in XOTcl, but in C. create distinguishes between the following situations:

  • Create a new object: create calls alloc and then doInitializations.
  • Recreate an existing object: When the specified object exists, it is recreated through the recreate method:
      givenClass recreate [self]
    

    recreate can be customized e.g. by overloading or interception. By default it calls cleanup followed by doInitializations.

In both cases, the method doInitializations is called automatically from C and has the following default behavior:

  • Search for parameter default values,
  • Call parameter initialization methods,
  • Call the constructor init.

Each step has a method call that can be changed, intercepted, etc. Of course, cleanup, recreate, instdestroy, etc. can also be overloaded or intercepted.

Consider a typical case for overloading recreate: a structure preserving recreate that cleans up the class but preserves the existing class hierarchy (subclass and instance relationships):

  Class StructurePreservingRecreate
  StructurePreservingRecreate instproc recreate {cl args} {
    if {[my isclass $cl]} {
      set subclass [$cl info subclass]
      set instances [$cl info instances]
    }
    next
    if {[my isclass $cl]} {
      foreach sc $subclass {
        $sc superclass $cl
      }
      foreach i $instances {
        $i class $cl
      }
    }
  }
  Object instmixin add StructurePreservingRecreate

Now the following code does not change the superclass or instance relationships of C:

  Class A
  Class B
  Class C -superclass {A B}
  Class D
  Class E -superclass {C D}
  C c1
  C c2

  # recreate -> is structure preserving
  Class C -superclass {A B}
  C c2

  # test
  puts superclass=[C info superclass]
  puts subclass=[C info subclass]
  puts instances=[C info instances]
  puts class=[c1 info class]
  puts class=[c2 info class]
Starting with XOTcl 1.4.0, xotcl provides also a user-friendly way for a structure-prevering recreate implemented in C. Since this version, one can configure "softrecreate" as follow.
::xotcl::configure softrecreate true
This command causes that recreates are structure-conservative.

Methods with Non-Positional Arguments

So far we have introduced methods only with positional arguments: that is, the position of an argument in the argument list determines to which local variable the argument is bound, when the method is invoked. Sometimes non-positional arguments -- arguments that carry both a name and a value when the method is invoked -- are useful. Before a non-positional argument can be used, it must be defined in the method definition using the following syntax:

 className instproc methodName ?non-pos-args? args body ?assertions
 objName proc methodName ?non-pos-args? args body ?assertions

The non-positional arguments are defined with the following syntax:

 {-name?:checkoption1, checkoption2, ...? default value} \
     {-name?:checkoption1, checkoption2, ...? ?default value?} ...

Only the name of the non-positional argument is really required, all other parts of this syntax are optional.

Let's consider a simple example, where a method with two non-positional args is defined; one has only a name ("a"), and one has a name and a default value (b):

 Object o
 o proc someproc {-a {-b {1 2 3}} x y} {
     puts "$a $b $x $y"
 }

We can invoke this method as follows:

 o someproc -b {4 5} -a 1 3 4

Here, the order of a and b can be changed; hence the name non-positional arguments. As b has a default value, we do not need to provide a value for it. In the following invocation b has the value "1 2 3":

 o someproc -a 1 3 4

The ordinary arguments start after the last non-positional argument (here: "3 4"). We can explicitly end the non-positional arguments by using "--". This is useful if we want to provide arguments that contain dashes ("-"), e.g.:

 o someproc -a 1 -- -b -c

Sometimes we want to check or control the non-positional arguments. For instance, in the above invocation, we might want to check that a is not forgotten, because otherwise the method cannot execute properly. This is the role of the checkoptions. There are three predefined checkoptions: required, boolean and switch. required checks whether a non-positional argument is given, boolean checks that a non-positional argument is of boolean type. For instance:

 Class P
 P instproc someproc {-a:required {-b:boolean true}} {
     puts "$a $b"
 }
 P p

This method requires a, and b needs to be of type boolean (is has the default value true). This invocation is valid:

 p someproc -a 1 -b 0

This invocation is invalid, because a is missing, and b is not a Tcl boolean type:

 p someproc -b " a b v"

The checkoption switch is similar to boolean except it does not require an additional argument. If the default value is false, the switch can be turned on, if the default is true it can be switched off.

The checkoptions are extensible. In fact, they are defined in an object ::xotcl::nonposArgs. We can extend this object with new methods. A check option method has the following syntax:

 someobject|someclass proc|instproc methodName {?optional nonpositional arguments? argName arg} {
  ...
 }

argName is here used to denote the name of the argument, and arg is the provided value.

Of course, the non-positional arguments can also be introspected. The following info options return the non-positional arguments of a method:

 objName info nonposargs methodName
 className info instnonposargs methodName

Message Interception Techniques

Even though object-orientation orders program structures around data, objects are characterized primarily by their behavior. Object-oriented programming style encourages the access of encapsulated data only through the methods of an object, since this enables data abstractions. A method invocation can be interpreted as a message exchange between the calling and the called object. Therefore, objects are at runtime only traceable through their message exchanges. At this point the message interceptors can be applied to catch and manipulate all incoming and outgoing messages of an object.

Generally interceptors can be applied to attach additional or extrinsic concerns to an object or a class or a class hierarchy. For instance roles or aspects can be implemented this way on various levels of scale.

We have already discussed some interception techniques implicitly. E.g., the unknown mechanism intercepts messages that have not be found on the object. It can be used as a very useful programming technique, e.g., the define a default behavior for an object. The interceptors presented in this section have a different character: They are applied before/after the original method even if the method is defined for the target object. Thus these interception techniques may be applied

We will discuss the message interceptors in this section in detail. The table below gives an impression, when which interceptor may be applied.

Message Interceptors Overview

Applied When

Primary Target Structure

Coverage

Per-Object Filter

before/after a call

object hierarchies

all methods

Per-Class Filter

before/after a call

class and class hierarchies

all methods

Per-Object Mixin

before/after a call

object

specific methods

Per-Class Mixin

before/after a call

class and class hierarchies

specific methods

Unknown Mechanism

after method was not found

object

all unknown calls


Filter

The filter (see [Neumann and Zdun 1999a] for more details) is a language construct to implement broader extensional concerns either for a single object or for several classes or class hierarchies. This way large program structures at the scale of several classes or class hierarchies can be managed. It is a very general interception mechanism which can be used in various application areas. E.g. a very powerful programming language support for certain design patterns is easily achievable, but there are also several other domains which are covered, like tracing of program structures, self-documentation at run-time, re-interpretation of the running program, etc.

A per-class filter is a special instance method that is registered for a class C. A per-object filter is a special instance method that is registered for a object o. Every time an object of class, C or the object o respectively, receives a message, the filter method is invoked automatically.

Usage of Filters

All messages to a filtered object must go through the filter before they reach their destination object. A simple example would be a sole filter on the class of the object. To define such a filter two steps are necessary. Firstly an filter method has to be defined, then the filter has to be registered. The filter method consists of three parts which are all optional. A filter method has the following form:

  className instproc FilterName args {
    pre-part
    next
    post-part
  }

When a filter comes to execution at first the actions in the pre-part are processed. The filter is free in what it does with the message. Especially it can (a) pass the message, which was perhaps modified in the pre-part, to other filters and finally to the object. It can (b) redirect it to another destination. Or it can (c) decide to handle the message on its own. The forward passing of messages is implemented through the next primitive of XOTcl. After the filter has passed its pre-part, the actual called method is invoked through next.

After the call of next is processed, the execution returns to the point in the filter, where the next call is located and resumes execution with the actions of the post-part. These may contain arbitrary statements, but especially may take the result of the actual called method (which is returned by the next-call) and modify it. The caller then receives the result of the filter, instead of the result of the actual called method.

The pre- and post-part may be filled with any ordinary XOTcl-statements. The distinction between the three parts is just a naming convention for explanation purposes.

The filter uses the args argument which lets us use a list of variable length as arguments, since it must filter a lot of different calls, which may have different argument lists. Furthermore, it may pass through arguments to other filters and the preceding filters may change the argument list.

Since any proc/instproc may be a filter, a registration of the filter is necessary, in order to tell XOTcl, which instprocs are filters on which classes. The filter and instfilter instance methods are able to handle this task for per-object filters and per-class filters respectively. Similar to the XOTcl language introduced so far, the filter registration is dynamic at run-time. By supplying a new list of filters to filter/instfilter, the programmer can change the filters registered on a class at arbitrary times. The filter instance method has the syntax:

  className instfilter filterList
for per-class filters and:
  objName filter filterList
for per-object filters.

Now a simple example should show the filter's usage. In the preceding examples we have defined several rooms. Every time a room action occurs it is likely that the graphical sub-system has to change something on the output of that particular room. Therefore, at first we need a facility to be informed every time an action on a room happens. This is quite easily done using filters:

  Class Room
  Room r1; Room r2;       # just two test objects

  Room instproc roomObservationFilter args {
    puts "now a room action begins"
    set result [next]
    puts "now a room action ends - Result: $result"
    return $result
  }

  Room instfilter roomObservationFilter

Now every action performed on room objects is notified with a pre- and a post-message to the standard output stream. We return the result of the actual called method, since we don't want to change the program behavior at all. E.g. we can set an instance variable on both of the two room objects:

  r1 set name "room 1"
  r2 set name "room 2"

The output would be:

  now a room action begins
  now a room action ends - Result: room 1
  now a room action begins
  now a room action ends - Result: room 2


  

Figure 4: Cascaded Message Filtering



All classes may have more than one filter. In fact they may have a whole filter chain, where the filters are cascaded through next. The next method is responsible for the forwarding of messages to the remaining filters in the chain one by one till all pre-parts are executed. Then the actual method is executed and then the post-parts come to turn. If one next-call is omitted the chain ends in this filter method. As an example for an additional filter we may register a filter that just counts the calls to rooms.

  Room set callCounter 0;  # set class variable
  Room instproc counterFilter args {
    [self class] instvar callCounter
    incr callCounter
    puts "the call number callCounter to a room object"
    next
  }
  Room instfilter {roomObservationFilter counterFilter}

Filters are invoked in registration order. The order may be changed by removing them and adding them in new order. Filters are inherited by sub-classes. E.g. in the preceding example for the next path, an OvalOffice was derived from the Room class. Without a change to the program each OvalOffice object automatically produces the same filter output as rooms.


  

Figure 5: Filter Inheritance


Filter chains can also be combined through (multiple) inheritance using the next method. When the filter chain of the object's class is passed, the filter chains of the superclasses are invoked using the same precedence order as for inheritance. Since on the subclass there may also be a another filter chain, without sophisticated computing in the pre- and post-parts one can produce easily a powerful tracing facility. E.g. if we want to distinguish an OvalOffice from other rooms we may want to add a filter solely for rooms of the type OvalOffice:

  Class OvalOffice -superclass Room
  OvalOffice o1;  # test object
  OvalOffice instproc ovalOfficeObservationFilter args {
    puts "actions in an oval office"
    next
  }
  OvalOffice instfilter ovalOfficeObservationFilter

A simple call to the o1 object, like:

  o1 set location "Washington"

produces the following output:

  actions in an oval office
  now a room action begins
  the call number 3 to a room object
  now a room action ends - Result: Washington

As seen already, filter registrations can be added dynamically at runtime. But they may also be removed. Perhaps the counting on rooms should stop after a while, then a simple call of the instfilter method is sufficient:

  Room instfilter roomObservationFilter

Filters can be removed completely by giving an empty list to the registration method:

  Room instfilter {}

Per-object filters operate on a single object. E.g. if we only want to observe a single Room object room1, we can use the filter method to register the roomObservationFilter only for this particular instance:

  room1 filter roomObservationFilter

As a filter we can register any method in the precedence order of the class or object. Thus we can also register procs as per-object filters. Additionally, meta-class methods may be registered as per-class filters. Filters are linearized so that each filter is only executed once, even if it is registered multiple times.



Introspection on Filters

In order to gain information about the currently registered filters on a certain object/class, the object info option filters and the class info option instfilters may be queried. It returns a list of the currently registered filters:

  className info instfilter
  objName info filter

A special call-stack info option for filters is self filterreg. It returns the name of the object or class on which the filter is registered. Since the filter may be registered on other objects/classes than the one on which it is defined, this may vary from self class in the filter. The command returns a list of the form:

  objName filter filterName
or:
  className instfilter filterName
respectively.



Example: A Simple Trace Filter

The trace example primarily demonstrates the inheritance of filter chains. Since all classes inherit from Object, a filter on this class is applied on all messages to objects. The Trace object encapsulates methods for managing the tracing:

  Object Trace
  Trace set traceStream stdout

  Trace proc openTraceFile name {
    my set traceStream [open $name w]
  }

  Trace proc closeTraceFile {} {
    close $Trace::traceStream
    my set traceStream stdout
  }

  Trace proc puts line {
    puts $Trace::traceStream $line
  }

  Trace proc add className {
    $className instfilter [concat [$className info filter] traceFilter]
  }

First we define the object and set a variable for the stream to which we send the trace outputs (here: stdout). With a method for opening and a method for closing a file we can redirect the trace stream to a file. puts is helper method for the filter to print an output to the selected output stream. In add the traceFilter is appended to the existing filters of a specified class. The actual filter method (see below) displays the calls and exits of methods with an according message. The calls are supplied with the arguments, the exit traces contain the result values. We have to avoid the tracing of the trace methods explicitly.

  Object instproc traceFilter args {
    # don't trace the Trace object
    if {[string equal [self] ::Trace]} {return [next]}
    set context "[self class]->[self callingproc]"
    set method [self calledproc]
    switch -- $method {
      proc -
      instproc {::set dargs [list [lindex $args 0] [lindex $args 1] ...] }
      default  {::set dargs $args }
    }
    Trace::puts "CALL $context>  [self]->$method $dargs"
    set result [next]
    Trace::puts "EXIT $context>  [self]->$method ($result)"
    return $result
  }

As trace message we write the callee´s context (class and proc), the invoked method (using calledproc), and the given arguments. In the switch statement we avoid to print whole method bodies.

With

  Trace add Room

messages to all rooms, including all instances of Room´s sub-classes, are surrounded with a CALL and an EXIT output. With

  Trace add Object

messages to all objects in an XOTcl environment are surrounded with a CALL and an EXIT output. In general, it is possible to restrict the trace to instances of certain classes, or to produce trace output for only certain methods. This requires registration methods and a more sophisticated implementation of the filter method.



Mixin Classes

Per-object and per-class mixins (see [Neumann and Zdun 1999c] for more details) are another interception technique of XOTcl to handle complex data-structures dynamically. Here, we use mixin as a short form for mixin class. All methods which are mixed into the execution of the current method, by method chaining or through a mixin class, are called mixin methods. Mixin classes resembles the filter presented in the preceding section. While the filters work on all calls to all methods of an object/class hierarchy, the mixin classes are applied on specific methods. The filter is defined in a single method, while the mixin is composes several method in a class.

Supplemental Classes

Mixin classes cover a problem which is not solvable elegantly just by the method chaining, introduced so far. To bring in an addition to a class, the normal XOTcl way is to define a mixin method and chain the methods through next, e.g.:

  Class Basic
  Basic instproc someProc  {
    # do the basic computations
  }
  Class Addition
  Addition instproc someProc {
    # do the additional computations
    next
  }

In order to mix-in the additional functionality of the supplemental class Addition a new helper class (sometimes called intersection class) has to be defined, like:

  Basic+Addition -superclass Addition Basic

This is even applicable in a dynamical manner, every object of the class Basic may be changed to class Basic+Addition at arbitrary times, e.g.:

  Basic+Addition basicObj
  ...
  basicObj class Basic+Addition

Now consider a situation with two addition classes. Then following set of classes has to be defined to cover all possible combinations:

  Class Basic
  Class Addition1
  Class Addition2
  Class Basic+Addition1 -superclass Addition1 Basic
  Class Basic+Addition2 -superclass Addition2 Basic
  Class Basic+Addition1+Addition2 -superclass Addition2 Addition1 Basic

The number of necessary helper classes rises exponential. For n additions, 2n-1(or their permutations if order matters) artificially constructed helper-classes are needed to provide all combinations of additional mix-in functionality. Furthermore it is possible that the number of additions is unlimited, since the additions may produce other additions as side-effects. This demonstrates clearly that the sub-class mechanism provides only a poor mechanism for mix-in of orthogonal functionality. Therefore we provide an extension in the form of object mixin classes, which are added in front of the search precedence of classes.

Per-Object Mixins

The mix-ins methods extend the next-path of shadowed methods. Therefore, per-object mix-in methods use the next primitive to access the next shadowed method. Consider the following example:

  Class Agent
  Agent instproc move {x y} { 
    # do the movement
  }
  Class InteractiveAgent -superclass Agent
  # Addition-Classes
  Class MovementLog
  MovementLog instproc move {x y} { 
    # movement logging
    next
  }
  Class MovementTest
  MovementTest instproc move {x y} {
    # movement testing
    next
  }

An agent class is defined, which allows agents to move around. Some of the agents may need logging of the movements, some need a testing of the movements, and some both (perhaps only for a while). These functionalities are achieved through the additional classes, which we will apply through per-object mixins.

Before we can use the per-object mix-ins on a particular object, we must register the mixins on it with the mixin instance method. It has the syntax:

  objName mixin mixinList

For example we may create two interactive agents, where one is logged and one is tested:

  InteractiveAgent i1; InteractiveAgent i2
  i1 mixin MovementLog
  i2 mixin MovementTest

At arbitrary times the mixins can be changed dynamically. For example i2's movements can also be logged:

  i2 mixin MovementTest MovementLog


  

Figure 6: Per-Object Mix-ins: Next-Path for the Example



The mixin option of the info instance method allows us to introspect the per-object mixins. It has the syntax:

  objName info mixin ?pattern?

It returns the list of all mix-ins of the object, if pattern is not specified, otherwise it returns the matching per object mixin classes.

The inverse operation of info mixin is mixinof finds out, into which objects an per-object mixin class is mixed into.
  clsName info mixinof ?pattern?

Note, that the constructors (init methods) of per-object mixins (and per-class mixins) are only called, if the mixin is registered already during object initialization (when init is called). For per-object mixins, one can achieve the initialization of a mixin via an idiom like

  Object o -mixin M -init
that registers the mixin before init is called. When a mixin is registered after object creation and it needs initializations, it is necessary to define special methods for this. Note, that the behavior described here is introduced in version 0.84 to ensure consistent behavior of intrinsic classes, per-object and per-class mixins, and to achieve predictable behavior for dynamic registration for all kind of mixins, and as well during recreations of objects having mixins registered. Older versions used heuristics for the initialization of per-object mixins.

Per-Class Mixins

Per-class mixins are exactly identical in their behavior to per-object mixins, but they operate on classes. Thus they are the class-specific variant of the per-object mixins, like instprocs are a class-specific variant of procs. Therefore, in the language the per-class mixins are called instmixins.

In general a per-class mixin is a class which is mixed into the precedence order of all instances of the class and all its subclasses it is registered for. It is also searched before the object's class itself is searched, but after per-object mixins.

Per-class mixins are linearized according to the precedence order like classes on the superclass hierarchy. I.e. from the full list of per-object mixins, per-class mixins, and intrinsic classes (and all the superclasses of all these classes) always the last occurrence is used.

From the point of view of language expressibility instmixins are not required, because they cannot express anything that per-object mixins cannot express already (like procs can express any instproc feature). As alternative to instmixins, we could simply register the per-object mixins in the constructor of the class.

But there at least the following reasons for instmixins as an additional language construct:

  1. we can at runtime determine with info mixin and info instmixin whether it is a class- or object-specific mixin. Thus we get a better structuring at runtime.
  2. We have not to 'pollute' the constructors with per-class mixin registrations. Therefore, the constructors get more understandable.
  3. If it is required to add (and remove) dynamically interceptors to a set of objects, which are instances of a certain type, per-class mixins are much easier to handle (e.g. add an instmixin to Object to intercept e.g. all calls to certain predefined methods).
  4. The language is more 'symmetrical', since any object-specific feature in XOTcl has a class-specific variant.

The mix-ins methods of per-class mixins extend the next-path of shadowed methods in the same way as per-object mixin methods. Before we can use a per-class mix-in on a particular class, we must register the mixin on it with the instmixin instance method. It has the syntax:

  className instmixin mixinList
The inverse operation of info inmixin is instmixinof finds out, into which objects an per-object mixin class is mixed into.
  className info instmixinof ?-closure? ?pattern?

Now consider that in the given per-object mixin example all interactive agents should be tested. We could either build a subclass TestedInteractiveAgent or register the per-object mixin in the constructor of the interactive agent class. The subclass solution leads to the same combinatorial explosion of intersection classes as discussed in the previous section, if more supplemental classes are added. The per-object mixin solution pollutes the constructor and does not prevail the structural semantics that the 'tested' property belongs to the interactive agent class at runtime

Here, we can use a per-class mixin:

  Class Agent
  Agent instproc move {x y} {# do the movement}
  Class InteractiveAgent -superclass Agent
  Class MovementTest
  MovementTest instproc move {x y} {
    # movement testing
    next
  }

  # now register the instmixin
  InteractiveAgent instmixin MovementTest

The per-class mixin now operates on all interactive agent including the instances of subclasses. E.g. for interactive agents i1 and i2 we automatically have movement testing. i2 is also logged, since it has the logging class as object-specific mixin:

  InteractiveAgent i1
  InteractiveAgent i2 -mixin MovementLog

  i1 move 3 4
  i2 move 1 2 

At arbitrary times the instmixins can be changed dynamically.

The instmixin option of the class info instance method allows us to introspect the per-class mixins. It has the syntax:

  className info instmixin ?className2?

It returns the list of all instmixins of the the class, if className2 is not specified, otherwise it returns 1, if className2 is a mixin of the object, or 0 if not.

Per-class mixins are applied transitively. That means the per-class mixin A of a per-class mixin B is also applied for an object in in B's scope. This is exactly the same as how superclasses are applied for instances. Consider the following example

  Class X11 \
     -instproc test args {
	puts [self class]
	next
     }
  Class X12 \
    -instproc test args {
	puts [self class]
	next
    }
  Class X \
    -instmixin {X11 X12} \
    -instproc test args {
	puts [self class]
	next
    }

  Class Y \
    -instmixin X

  Y create y -test
  X create x -test

Here the application as a superclass (for x) yields the same result as the application as an instmixin (for y):

  ::X11 
  ::X12 
  ::X

Precedence Order

The precedence order is composed by the precedence order of the superclass hierarchy (as explained earlier) and the message interceptors. In general, filters precede mixins and the superclass hierarchy. They are applied in the order of the next path of the object. Thus per-object filters are ordered before per-class filters.

Mixins are processed after the filters. Again, they are applied in the order of the next path of the object. Thus per-object mixins are ordered before per-class mixins.

Finally, the object's own heritage order comes in the order: object, class, superclasses.

The three precedence order lists (filters, mixins, and classes) are pre-calculated and cached.

Filters as well as classes (mixins and ordinary classes) are linearized. That means, each filter and each class can be only once on a precedence order list. If a filter or class can be reached more than once, than the last occurrence is used.

For instance, consider a class A is superclass, per-class mixin, and per-object mixin. On the precedence order lists only the last occurrence as a superclass is used after linearization.

Guards for Filters and Mixins

Message interceptors, such as filters and mixins, are applied for potentially huge number of messages. In many cases it is possible to reduce the effective number of cases in which interceptors are applied. Interceptor guards offer this functionality: they are boolean conditions with which you can specify in which cases a registered interceptor should be applied.

Filter Guards

A filter guard is a set of conditions that determine whether a filter is to be executed upon a certain invocation or not. Syntactically we can append a filter guard to the filter registration, or it can be registered using the methods filterguard for filters and instfilterguard for instfilters.

Each filter guard is an ordinary condition. A filter guard is executed in the call frame of the filter to be executed, if the filter guard returns 1. Thus, the call-stack information are already set to the values of the targeted filter - and these values can be used in the filter guard.

Let us consider a simple program:

Class Room
Room instproc enter {name} {puts [self proc]}
Room instproc leave {name} {puts [self proc]}
Room instproc loggingFilter args {
    puts [self calledproc]
    next
}
Room instfilter loggingFilter

Now consider we only want to apply the logging filter for enter and leave, not for any other message sent to Room instances. In the following example, for instance, we do not want to log the set message:

Room r 
r enter Uwe
r leave Uwe
r set roomName "Office"

In this example a filterguard can be applied to restrict the application of the filter to those two methods:

Room instfilterguard loggingFilter {
  [self calledproc] == "enter" || 
  [self calledproc] == "leave"}

Here we limit the filter application of the logging filter on rooms to calls to enter and leave. All other calls are not filtered at all. Note that the same syntax can also be applied for filterguard. Also, there is a short form to register filter guards directly during filter registration. The following code has the same semantics as the filter and filter guard definitions above:

Room instfilter {{loggingFilter -guard {
    [self calledproc] == "enter" || 
    [self calledproc] == "leave"}}}

The filter guard language construct is registration centric. It only applies for the class or object on which a filter is registered, not for all applications of the filter method. That is, if we use loggingFilter on another class we may give no or completely different filter guards.

If no filter guard is given for a filter, we assume that it is to be applied on all methods (equivalent to the filter guard '1' which is always true).

There are introspection options for filter guards. In particular, we can use info filterguard and info instfilterguard for getting the filter guards for a particular filter or instfilter respectively. For instance:

puts [Room info instfilterguard loggingFilter]

This prints out the content of the above guard definition. We can also append -guard to info filter or info instfilter to obtain a filter definition with guards:

puts [Room info instfilter -guards]

If we call a method from within a filter guard, as for instance callsMethod, we might require some parameters from the guard's context, such as calledproc. These parameters can be passed as references, as in the following example:

  Room instfilter loggingFilter
  Room instfilterguard loggingFilter {[my callsMethod openURL [self calledproc]]}

This example works because the filterguard is already set to the scope of the guard. Now we can use this dynamic calledproc context in the called method:

  Room instproc callsMethod {method calledproc} {
    return[string match $calledproc $method]
  }

We simply check whether the called method matches the given method name or not.

Mixin Guards

Similar to filters, there are mixin guards, defined with mixinguard and instmixinguard, or with -guard during mixin registration. Consider a simple example: there are a number of birds who have two mixins: Fly and Sing. For Fly there are limitations: a bird can only fly if it is at least two years old and is not a Penguin. Such problems are be solved with mixin guards:

  Class Fly
  Fly instproc fly {} {puts "[my signature]: yippee, fly like an eagle!"}

  Class Sing
  Sing instproc sing {} {puts "[my signature]: what a difference a day makes"}

  Class Animal -parameter age
  Animal instproc unknown args { puts "[my signature]: how should I $args?"}
  Animal instproc signature {} {
    return "[self] [my info class] ([my age] years)"
  }

  Class Bird -superclass Animal
  Class Penguine -superclass Bird
  Class Parrot -superclass Bird
  Class Duck -superclass Bird

  Parrot tweedy -age 1
  Penguine pingo -age 5
  Duck donald -age 4
  Parrot lora -age 6

  Bird instmixin {{Fly -guard {[my age] > 2 && ![my istype Penguine]}} Sing}

An invocation like:

foreach bird {tweedy pingo donald lora} { $bird fly }

yields the following result:

::tweedy ::Parrot (1 years): how should I fly?
::pingo ::Penguine (5 years): how should I fly?
::donald ::Duck (4 years): yippee, fly like an eagle!
::lora ::Parrot (6 years): yippee, fly like an eagle!

There are similar introspection options for mixin guards as those for filter guards. In particular, we can use info mixinguard and info instmixinguard for getting the mixin guards for a particular mixin or instmixin respectively.

Querying, Setting, Altering Filter and Mixin Lists

The methods mixin, instmixin, filter and instfilter are system slots having the same query and update interface.
  • If one of those methods is called without argument, it returns the current setting.
  • If it is called with one argument, the argument is used to set the specified list as indicated in the above examples.
  • If these methods are called with more than one argument, the first argument is used to specify the action. Possible values for the action are set, get, add and delete. See below for commonly used examples.

obj mixin same as: obj info mixin
obj mixin {C1 C2} same as: obj mixin assign {C1 C2}
obj mixin assign {C1 C2}sets the mixins for obj
obj mixin add C3 adds the mixin C3 on front of the mixin list
obj mixin add C3 end adds the mixin C3 at the end the mixin list
obj mixin add C3 3 adds the mixin C3 at the 3rd position
obj mixin delete ::C3removes the mixin C3 from the mixin list. Use absolute class names. delete supports an optional flag -nocomplain that does not produce an error, when the specified class is not in the list.

Note, that the list of possible actions can be extended by extending the class ::xotcl::Relations.

Querying Call-stack Information

Since the presented interceptors are normal XOTcl instprocs they can access all XOTcl introspection abilities introduced so far. In instprocs all recent information is accessible within their scope. But the interceptors are mechanisms, which cover more then their sole scope. The meaningful usage of the meta-programming abilities often requires to go further and to get information from the caller's and the callee's scope (e.g for delegation decisions). Therefore, we introduced rich call-stack informations for the interceptors. Note, that these are also available for ordinary methods, but the "called..." info options return empty strings.

All call-stack information are packed compactly into the self primitive as additional options. Note, before XOTcl version 0.84 these were implemented as a part of the info method. They are part of the self command for conceptual integrity: introspection options in info can be expected to produce the same result, when they are not explicitly changed. In contrast, all information provided by self are call-stack dependent.

Querying Call-stack Information via self

self activelevel

Returns the stack level from where the current command was invoked from, or where the last next was called (whatever is closer to the invocation). If the current command was invoked from an XOTcl method the absolute level is returned (e.g. #4) which can be used in the uplevel or upvar Tcl command or XOTcl method. If the current command was not invoked from an XOTcl method, the value 1 is returned.

self calledproc

Returns the name of the method which was invoked in the original call.

self calledclass

Returns the name of the class which presumably (if no dynamic class change occurs afterwards) is invoked in the original call.

self callingclass

Returns the name of the class from which the call was invoked (if one exists, otherwise an empty string).

self callinglevel

Returns the stack level from where the current command was invoked from. In contrary to activelevel next-calls are ignored in the computation. If the current command was invoked from an XOTcl method the absolute level is returned (e.g. #4) which can be used in the uplevel or upvar Tcl command or XOTcl method. If the current command was not invoked from an XOTcl method, the value 1 is returned.

self callingproc

Returns the name of the method from which the call was invoked (if one exists, otherwise an empty string).

self callingobject

Returns the name of the object from which the call was invoked (if one exists, otherwise an empty string).

self filterreg

In a filter: returns the name of the object/class on which the filter is registered. Returns either 'objName filter filterName' or 'className instfilter filterName'.

self isnextcall

Return 1 if this method was invoked via next, otherwise 0

self next

Return the "next" method on the path as a string, i.e. the method which will be called by [next].


Note, that three options with the prefix calling represent the values of self, self proc, and self class in the scope where the original call was invoked. In the following section we will show a simple program in which all of the info options have different values.


Filter Call-stack Information Example

Now we discuss a simple example that shows that all filter introspection options may have different values:

  Class InfoTrace
  InfoTrace instproc infoTraceFilter args { 
    puts "SELF:                [self]"
    puts "SELF PROC:           [self proc]"
    puts "SELF CLASS:          [self class]"
    puts "INFO CLASS:          [my info class]"
    puts "CALLED PROC:         [self calledproc]"
    puts "CALLING PROC:        [self callingproc]"
    puts "CALLING OBJECT:      [self callingobject]"
    puts "CALLING CLASS:       [self callingclass]"
    puts "REGISTRATION CLASS:  [self filterreg]"
    puts "CALLING LEVEL:       [self callinglevel]"
    puts "ACTIVE LEVEL:        [self activelevel]"
    next
  }

  Class CallingObjectsClass
  CallingObjectsClass callingObject

  Class FilterRegClass -superclass InfoTrace
  Class FilteredObjectsClass -superclass FilterRegClass 
  FilteredObjectsClass  filteredObject 

  CallingObjectsClass instproc callingProc args {
     filteredObject set someVar 0
  }    
  FilterRegClass instfilter infoTraceFilter

The invocation of callingObject callingProc produces the following output:

  SELF:                ::filteredObject
  SELF PROC:           infoTraceFilter
  SELF CLASS:          ::InfoTrace
  INFO CLASS:          ::FilteredObjectsClass
  CALLED PROC:         set
  CALLING PROC:        callingProc
  CALLING OBJECT:      ::callingObject
  CALLING CLASS:       ::CallingObjectsClass
  REGISTRATION CLASS:  ::FilterRegClass instfilter infoTraceFilter
  CALLING LEVEL:       #1
  ACTIVE LEVEL:        #1

The filter reports for self the value filteredObject, since this is the object on which the set call is invoked; infoTraceFilter is the method of the filter, and therefore, the actual proc, while the actual class is InfoTrace, the filter's class. The class of the actual object is FilteredObjectsClass.

The called procedure is set. While the program stays in a XOTcl-instproc all calling-info-options are set, the calling procedure is callingProc, the calling class is the class, where the method is defined (namely CallingObjectsClass), and the object from which the call invoked is callingObject.

In this example, the calling level is equal to the active level, both are #1.

Slots

A slot is a meta-object that manages prop