XOTcl Tutorial
XOTcl Tutorial
1.6.4
1
XOTcl - Tutorial
⋅
Introduction
⋅ Language Overview
⋅ Introductory Overview Example: Stack
⋅ Object specific methods
⋅ Refining the behavior of objects and classes
⋅ Stack of integers
⋅ Class specifc methods
⋅ Introductory Overview Example: Soccer Club
⋅ Object and Class System
⋅ Basic Functionalities
⋅ Objects
⋅ Data on Objects
⋅ Methods for Objects
⋅ Information about Objects
⋅
Classes
⋅ Creating Classes and Deriving Instances
⋅ Methods Defined in Classes
⋅ Information about Classes
⋅ Inheritance
⋅ Destruction of Classes
⋅ Method Chaining
⋅ Dynamic Class and Superclass Relationships
⋅ Meta-Classes
⋅ Create, Destroy, and Recreate Methods
⋅ Methods with Non-Positional Arguments
⋅
Message Interception Techniques
⋅ Filter
⋅ Mixin Classes
⋅ Precedence Order
⋅ Guards for Filters and Mixins
⋅ Querying, Setting, Altering Filter and Mixin Lists
⋅ Querying Call-stack Information
⋅ Slots
⋅ System Slots
⋅ Attribute Slots
⋅ Setter and Getter Methods for Slots
⋅ Backward-compatible Short-Hand Notation for Attribute Slots
⋅ Experimental Slot Features
⋅ Value Checking
⋅ Init Commands and Value Commands for Slot Values
⋅ Nested Classes and Dynamic Object Aggregations
2
XOTcl - Tutorial
⋅ Nested Classes
⋅ Dynamic Object Aggregations
⋅ Relationship between Class Nesting and Object Aggregation
⋅ Simplified Syntax for Creating Nested Object Structures
⋅ Copy/Move
⋅ Method Forwarding
⋅ Assertions
⋅
Additional Functionalities
⋅ Abstract Classes
⋅ Automatic Name Creation
⋅ Meta-Data
⋅ Integrating XOTcl Programs with C Extensions (such as Tk)
⋅ References
3
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.
Extended Object Tcl 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:
Language Overview 5
XOTcl - Tutorial
In a first step, we define a class Stack. A new class is defined in XOTcl via the command Class
create yourclass. The stack will have a constructor (in XOTcl, the method init) and the methods
push and pop. Methods which are inherited to objects are defined via instproc. In the following
example, all predefined commands (some from Tcl, some from XOTcl) are emphasized.
#
# Create a stack class
#
Class create Stack
The definition of the class Stack is typically saved in a file (say stack.xotcl) and can be used e.g. in
an interactive Tcl shell (tclsh) as follows. The percent sign indicates the prompt of the Tcl shell, the
reminder of the line is typed in, the result of the command is shown in the line below. Comments are
lines starting with a hash symbol #.
In the session above, we load XOTcl into the current shell, import the names from the xotcl
namespace and we load the file stack.xotcl. At this time, the class Stack is available in the scripting
session. In the next step, we create an stack object named s1 and push into the stack the values a,
b and c via separate push calls. Then we pop values from the stack and we destroy finally the stack
s1.
#
# Create an object named stack
#
Object create stack
The object stack can be used in exactly the same way as s1 (the instance of class Stack) before.
#
# Create a safety class
#
Class create Safety
Safety instproc init {} {
my set count 0
next
}
Safety instproc push {thing} {
my incr count
next
}
Stack create s1
Stack create s2
Before we start, we introduce an instrument for making the documentation of programs more easy.
In order to document source code files, 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 later 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. Classes 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.
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).
The PlayerTrainer uses multiple inheritances by being both a player and a trainer:
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).
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.
Finally we define two convenience to print the members/players to the stdout with puts.
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:
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.
Class Singer
Singer instproc sing text {
puts "[my name] sings: $text, lala."
}
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).
Now we can re-class the player object to its new class (now Franz Beckenbauer is President of
Bayern Munich.
But still Franz Beckenbauer can entertain us with what he believes is singing:
bayernMunich printPlayers
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):
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:
chelsea printPlayers
bayernMunich printPlayers
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).
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
Objects 15
XOTcl - Tutorial
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.
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):
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
kitchen destroy
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:
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
Data on Objects 16
XOTcl - Tutorial
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:
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:
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
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
Similarly Tcl´s incr command is mapped to the object system. A call with the syntax:
increments varName with the given value (or without given value with 1).
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:
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
}
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.
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:
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.
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:
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.
Inheritance 23
XOTcl - Tutorial
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.
Destruction of Classes 24
XOTcl - Tutorial
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:
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:
Method Chaining 25
XOTcl - Tutorial
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.
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]
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
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
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.
Meta-Classes 27
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.
Class Foo
it has no private variables and no special methods. This is somewhat similar as creating an object
XOTcl - Tutorial
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.
Dog piffie
Dog idefix
puts "nr of dogs: [Dog counter]"
piffie destroy
Class c
Object c ;# here, "c" is downgraded to an object, no "recreate" is cal
For create and recreate, the method doInitializations is called automatically from C and has the
following default behavior:
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
# test
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:
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:
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:
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:
Usage of Filters 35
XOTcl - Tutorial
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
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:
Usage of Filters 36
XOTcl - Tutorial
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.
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.
Usage of Filters 37
XOTcl - Tutorial
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:
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:
Usage of Filters 38
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:
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
XOTcl - Tutorial
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.
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
messages to all rooms, including all instances of Room´s sub-classes, are surrounded with a CALL
and an EXIT output. With
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:
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 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
Mixin Classes 41
XOTcl - Tutorial
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:
For example we may create two interactive agents, where one is logged and one is tested:
Supplemental Classes 42
i2 mixin MovementTest
At arbitrary times the mixins can be changed dynamically. For example i2's movements can also be
logged:
The mixin option of the info instance method allows us to introspect the per-object mixins. It has the
syntax:
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
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:
we can at runtime determine with info mixin and info instmixin whether it is a class- or
XOTcl - Tutorial
Class InteractiveAgent -superclass Agent
Class MovementTest
MovementTest instproc move {x y} {
# movement testing
next
}
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
The instmixin option of the class info instance method allows us to introspect the per-class mixins. It
has the syntax:
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
}
Per-Class Mixins 45
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.
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:
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:
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:
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:
Filter Guards 47
XOTcl - Tutorial
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:
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:
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"}
Bird instmixin {{Fly -guard {[my age] > 2 && ![my istype Penguine]}} Sing}
An invocation like:
Mixin Guards 48
yields the following result:
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.
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
XOTcl - Tutorial
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
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.
In this example, the calling level is equal to the active level, both are #1.
We distinguish between system slots (predefined slots like class, superclass, mixin, instmixin, filter,
instfilter) and attribute slots (e.g. attributes of classes).
System Slots
System slots are predefined slots defining e.g. some relations between classes, or between objects
and classes. The predefined system slots are:
superclass: every class in XOTcl has one or more superclasses. The name of this slot is
superclass, the domain is ::xotcl::Class, the slot is multivalued, since one object might have
Attribute Slots
Attribute slots are used to manage the setting and querying of instance variables. We define now a
person with three attributes name, salary and projects.
These attributes might have a default value or they might be multivalued. When an instance of class
Person is created, the slot names can be used for specifying values for the slots.
Object p1 has three instance variables, namely name, salary and projects. Since slot projects is
multivalued, we can add a value to the list of values the add subcommand.
The value of the instance variable project of Person p1 is now the list {some-other-value ::project1}.
Attribute slots are implemented via dynamic object aggregations (see below), where the Class
objects contain the slot objects with the information like default etc. In order to prevent name
clashes between the slot objects and the methods of a class (like e.g. create), an intermediary
object named slot is used as a container of the slot objects. In the example above we create an
object structure of the following form:
Person
Person slot name
Person slot salary
Person slot projects
This object structure can be used to to query and modify the slot properties or to add additional
methods to the slot objects. One application is for example to used slot-specific methods for
checking slot values, as shown in the next section.
However, due to the current implementation, it is necessary to re-init the slot object when the slot
properties (such as e.g. default) are changed. This can be achieved by calling the method init of the
slot object.
... {
Attribute name
...
}
... {
Attribute create name
...
}
This is exactly the same situation like every where else in XOTcl, when an object/class is created.
One has to use create explicitly, when a name of a slot object conflicts with a method of the class
(e.g. one has to use "Attribute create class" if a slot named class is created).
One cannot define on a meta-class an attribute named slot or slots and use then "... MetaClass Foo
-slots { ::xotcl::Attribute x}... to create the slot objects. To handle this naming conflict, one has to
create the slot objects outside of the aggregation and to provide the values for the properties of
Attribute (domain, manager, .... ) by hand.
A create a1
? {a1 foo 10} 10
? {a1 foo} 10
? {catch {a1 foo -1}} 1
For the most common simple cases with single valued attributes, where neither setter or getter are
redefined, XOTcl optimizes the slot access function and replaces the delegation to the slot object by
the the C-level implementation of instparametercmd.
Note that it is possible to subclass Attribute (e.g. in order to store more properties for attributes, like
when attributes are stored in a relational database) or to register mixin-classes or filters.
The various features of the prior implementation of parameter are deprecated and will be removed
in future versions.
It is as well possible to define custom value checkers and to normalize the input values. We extend
the previous example and define "my sex" as value for type. If the value checker consists of multiple
words, the type check compiler assumes that the value is a Tcl command, to which the actual value
is appended as additional argument before invocation. my refers to the slot object. In the example
below, we define for the slot object an object specific method that returns 1 or 0 depending on the
success of the check. This method (a) checks the values via switch and (b) normalizes and resets
the value via uplevel.
The slot values are actually checked via Tcl variable traces whenever the associated variable gets a
new value assigned. This means that the values are enforced now matter how the variables are set.
Therefore, the checks are performed in the following two commands as well, although the slot
values are not accessed via the slot names. The checks will throw an error in the second command,
since 1100x is not an integer.
Similarly the second command below will throw an error, since some-other-value is not an instance
of ::Project.
When a check throws an error, the instance variables are reset to the previous value. To restore the
original value, an associative array __oldvalue() is kept as instance variable in the object.
This mixin replaces the methods check and checkall as well as mk_type_checker by no-ops. When
the mixin is active and the Attribute definitions are loaded, the specified type has no effect.
Value checking can be turned off also selectively for each slot via using ::xotcl::Slot::Nocheck as
per-object-mixin; if attributes are subclassed, it is possible to register the Nocheck mixin on a
subclass of Attribute.
The primary advantage of slot init commands is Lacy initialization: When an object has many
XOTcl - Tutorial
C c1
c1 info vars ;# ==> returns ""
c1 set x ;# ==> puts init, returns 101
c1 info vars ;# ==> returns "x"
The initcmd is executed only once, when the variable is read the first time. For later reads of the
variable contents, the values are returned.
A value command (valuecmd) of a slot is similar to a a init command, except that it is executed
whenever the value of the variable is read. A value command can be used e.g. to implement live
updates for variables or for abstracting from sql sequences or the like.
Finally the value changed command (valuechangedcmd) can be used to specify the behavior,
whenever the value of the variable is altered. This option is used to implement the value checking
described in the last section.
The slot parameters default, initcmd and valuecmd have to be used mutually exclusively.
In XOTcl every object and every class is logically implemented as a separate Tcl namespace. The
biggest benefit of this design decision aside from performance advantages is the ability to
aggregate objects and nest classes. Contrary in OTcl every object has a global identifier. Through
the introspection abilities of namespaces nested classes are also traceable at runtime and can be
changed dynamically. In XOTcl objects are allowed to contain nested objects, which are
dynamically changeable aggregates of the containing object.
Nested Classes
The notation for nested classes follows the syntax of Tcl namespaces by using ``::'' as a delimiter.
For example the description of a oval carpet and a desk can nest inside of the OvalOffice class:
Class OvalOffice
# general carpet
Class Carpet
Class OvalOffice::Desk
# special oval carpet - no name collision
Class OvalOffice::Carpet -superclass ::Carpet
Nested classes can be used exactly like ordinary classes, a user can sub-class it, derive instances,
etc. The information about the nesting structure of classes is available through the info instance
method:
The classchildren option returns a list of children, if one or more exist, otherwise it returns an empty
string. classparent results in the name of the parent class, if the class is nested. Since nested
classes are realized through namespaces, all functionality offered by Tcl's namespace command is
usable from XOTcl as well.
Nested Classes 59
XOTcl - Tutorial
ClassAgent
Agent myAgent
Class Agent::Head
Class Agent::Body
Agent::Head ::myAgent::myHead
Agent::Body ::myAgent::myBody
Now the objects myHead and myBody are part of the myAgent object and they are accessible
through a qualification using ``::'' (or through Tcl's namespace command). But in the common case
they will be accessed, as introduced so far: the explicit full qualification is not necessary when such
variables are being accessed from within XOTcl methods, since the object changes to its
namespace.
The information about the part-of relationship of objects can be obtained exactly the same way as
for classes through the info interface:
Now all agents derived from the class have the two property objects aggregated after creation. But
still they are changeable in a dynamical manner, e.g. with:
Agent myAgent
myAgent::myHead destroy
The agent turns into a headless agent. In companion of the introspection mechanisms such
constructions could be very useful. Suppose, that in the virtual world the agents heads may be
In general, delegation can be achieved in XOTcl without any special construct using simple
methods containing a few lines. However, In several situations, forwarding is as well needed to plain
Tcl commands, for example, if object oriented stubs are implemented on base of non-oo function
calls. These functions might access instance variables of the objects. XOTcl uses this functionality
in various situations, such as for instance in the implementation of the set, unset, append, array
methods among others.
The fowarding functionality is suppored by XOTcl be the methods forward and instforward that
address these requirements and provide an efficient implementation for these tasks.
The forwarding command specifies that whenever methodName is called, this invocation is
delegated to callee, where the actual argument list (from the invocation) is appended to the
argument list specified in the forwarding command. Like for procs and instprocs, we can distinguish
between forward and instforward, depending on we want to the method available for a single object
of for the instances of a class.
(optional) method name and optional arguments. More about this later. Here we register the
forwarder under the name wag, the callee is tail, and the method is defined to have the name of the
forwarder. We could have written here dog forward wag tail wag as well, be we use %proc which
refers to the name of the forwarder. Using %proc is slightly more general in cases the forwarder is
renamed.
###########################################
# trivial object delegation
###########################################
Object dog
Object tail
tail proc wag args { return $args }
dog forward wag tail %proc
With these definitions a call to "dog wag 100" calls actually "tail wag 100" which returns the result of
100.
The following command shows the delegation to a Tcl command (instead of delegation to an
object). We define a simple forwarder that forwards a call to the Tcl command expr with some
arguments.
###########################################
# adding
###########################################
Object obj
obj forward addOne expr 1 +
The invocation obj addOne 5 returns 6 as value.
In our next example we want additionally that the Tcl command should to be evaluated in the
context of the current object. This means that the method can easily access instance variables of
the delegating object. We define a forwarder for the class X with the name Incr (to avoid confusion
with the already defined method incr), we use the -objscope option and specify incr as the callee.
Since the forwarder is defined via instforward the forwarder is available to all instances of the class.
###########################################
# evaluating in scope
###########################################
Class X -parameter {{x 1}}
X instforward Incr -objscope incr
X x1 -x 100
x1 Incr x
x1 Incr x
x1 Incr x
After the three calls to Incr the call x1 x returns the value 103.
In our next example, we show the usage of the %-substitution more advanced argument handling.
This example sketches the implementation of the mixin add, mixin set methods as shown above. In
order to obtain extensible subcommands (such as mixin add, mixin delete, etc.), we define an object
for which the subcommands are defined as methods. We will use this object as callee for the
Copy/Move 64
appropriate methods. So, we define an object named mixin and define a forwarder with the name
Mixin (again we capitalize Mixin to avoid name clashes with the already defined methodmixin ).
###########################################
# mixin example
###########################################
Object create mixin
mixin proc unknown {m args} {return [concat [self] $m $args]}
obj forward Mixin mixin %1 %self
We define here the method unknown to see what arguments are passed. The following invocation
Object instforward Info -methodprefix @ Info %1 %self
With this definitions, the following call is rewritten as indicated in the comment.
x1 Info class ;# ::Info @class ::x1
When a forwarder is defined, the callee (the target command) can be omitted. When the callee is
not specified, the method-name is used instead. When the method-name has a namespace prefix,
the method name is the tail and the callee is the fully qualified name.
###########################################
# optional callee
###########################################
obj set x 2
obj forward append -objscope
Object n; Object n::x
obj forward ::n::x
With this definitions of the forwarder append and x, the following calls are rewritten as indicated in
XOTcl - Tutorial
The construct %argclindex LIST can be used to substitute an argument depending on the number of
arguments when the forwarder is invoked. For example, it is possible to call forward to a different
method depending on how many arguments are specified. The number of arguments is used as an
index in the specified list. When the number of arguments is larger than the number of elements in
the specified list, an error is generated.
###############################################
# substitution depending on number of arguments
###############################################
obj forward f %self [list %argclindex [list a b c]]
obj proc a args {return [list [self proc] $args]}
obj proc b args {return [list [self proc] $args]}
obj proc c args {return [list [self proc] $args]}
? {obj f} [list a {}]
? {obj f 1 } [list b 1]
? {obj f 1 2} [list c {1 2}]
? {catch {obj f 1 2 3}} 1
Finally, the concluding example defines a class chan to use the I/O-commands in an OO-manner.
The proc open is used to create a chan instance. For the channel object we provide the method
close (to close a channel and to destroy the channel object), puts (to write on a stream), blocked (to
check whether last command exhausted all input), and fconfigure (to configure the stream). Note
that for puts we specified that the actual stream should be inserted as the second to last argument.
Copy/Move 67
XOTcl - Tutorial
Assertions
In order to improve reliability and self documentation we added assertions to XOTcl. The
implemented assertions are modeled after the ``design by contract'' concept of Bertrand Meyer. In
XOTcl assertions can be specified in form of formal and informal pre- and post-conditions for each
method. The conditions are defined as a list of and-combined constraints. The formal conditions
have the form of normal Tcl conditions, while the informal conditions are defined as comments
(specified with a starting ``#''). The lists containing the pre- and post-conditions are appended to the
method definition (see example below).
Since XOTcl offers per-object specialization it is desirable to specify conditions within objects as
well (this is different to the concept of Meyer). Furthermore there may be conditions which must be
valid for the whole class or object at any visible state (that means in every pre- and post-condition).
These are called invariants and may be defined with following syntax for class invariants:
Logically all invariants are appended to the pre- and post-conditions with a logical ``and''. All
assertions can be introspected.
Copy/Move 68
XOTcl - Tutorial
Since assertions are contracts they need not to be tested if one can be sure that the contracts are
fulfilled by the partners. But for example when a component has changed or a new one is
developed the assertions could be checked on demand. For this purpose the check method can be
used either to test the pre- or the post-conditions. The syntax is:
Per default all options are turned off. check all turns all assertion options for an object on, an
arbitrary list (maybe empty) can be used for the selection of certain options. Assertion options are
introspected by the info check option. The following class is equipped with assertions:
The parameter instance method defines an instance variable value with value 1. The invariant
expresses the condition (using the Tcl command regexp), that the value must be a single decimal
digit. The method definition expresses the formal contract between the class and its clients that the
method incrValue only gets input-states in which the value of the variable value is positive. If this
contract is fulfilled by the client, the class commits itself to supply a post-condition where the
variable's value is larger than 1. The formal conditions are ordinary Tcl conditions. If checking is
turned on for sensor s:
s check all
the pre-conditions and invariants are tested at the beginning and the post-condition and invariants
are tested at the end of the method execution automatically. A broken assertion, like calling
incrValue 9 times (would break the invariant of being a single digit) results in an error message.
In assertions we do not check methods that modify or introspect assertions. These are
check,info,proc,instproc,invar, and instinvar. The reason for this is that we want to be able to
recover a malicious action in a catch error handler, like:
...
if {[catch {my assertionBreakingAction} errMsg]} {
puts "CAUGHT ERROR: $errMsg"
# remember checking options, for turning them on later again
set check [my info check]
my check {}
# recover from broken assertion
...
Copy/Move 69
XOTcl - Tutorial
# turning checking on again
$fb check $check
}
Copy/Move 70
XOTcl - Tutorial
Older versions of XOTcl have contained a special meta-data command metadata. This command is
now (from version 0.83) deprecated and replaced by an integrated solution with XOTcl's API
documentation functionality. The object @ is used for documentation and metadata issues. Per
default it is not evaluated at all. Everything that is send to @ is simply ignored. That way we do not
waste memory/performance at runtime, if we do not require to parse the metadata/documentation.
If we have to know the meta-data/documentation, as for instance in the xoDoc component and the
makeDoc tool, that handle XOTcl's internal documentation, we have to re-define the documentation
object. Alternatively, we can partially parse the source code for @ commands.
With @ the meta-data/documentation is handled by first class XOTcl objects. By defining alternate
@ implementations - as in xoDoc/makeDoc - we can evaluate the meta-data/documentation
arbitrarily. xoDoc/makeDoc are only an HTML back-end, but the basic idea is to provide support for
several other usages as well (e.g. XML, RDF, on-line help, documentation of dynamic structures,
etc).
The object@ handles comments via its unknown method. xoDoc adds the appropriate instprocs to
t@ to produce HTML output. The appropriate command is:
The source of a documentation is structurally very similar to the XOTcl constructs being
commented. E.g. one can copy an instproc and add comments at the right places, like:
Class C
C instproc m {a1 a2} {
return [expr {$a1+$a2}]
}
One can do essentially a copy+paste of the source and add the comments via attribute value pairs.
Every basic language construct can have a "description". If you want to include other properties to
the description, you can add them like:
Copy/Move 71
XOTcl - Tutorial
This way, author and date are added automatically to the generated HTML file. In addition, there is
a @File hook for a per file description, like:
@ @File {
description {
This is a file which provides a regression test
for the features of the XOTcl - Language.
}
}
Additional Functionalities
Abstract Classes
In XOTcl a class is defined abstract if at least one method of this class is abstract. The instance
method abstract defines an abstract method and specifies its interface. Direct calls to abstract
methods produce an error message. E.g. a Storage class provides an abstract interface for access
to different storage forms:
Class Storage
Storage abstract instproc open {name}
Storage abstract instproc store {key value}
Storage abstract instproc list {}
Storage abstract instproc fetch key
Storage abstract instproc close {}
Storage abstract instproc delete {k}
All kinds of storage have to implement every method from the interface. E.g. a GNU Database
Access, a relational database access, and several other storage forms may be derived by
sub-classing (therefore, all conform to the same storage access interface).
If one can be sure that a command represents an object, it might be unsure if the command is only
an object or also class or even meta-class. The two instance methods isclass and ismetaclass
check in the same manner, whether a class or meta-class is given (since ever XOTcl class is an
object, they also return 0, when objName is not an XOTcl object).
Abstract Classes 72
objName1 ismetaclass objName2
Exit Handler
A task for a programming language, sometimes of similar importance as object creation, is the
object destruction. XOTcl ensures that all objects are destroyed and their destructors are invoked
when XOTcl applications terminate. For that reason objects and classes are destroyed in the order
objects, classes, meta-classes. Sometimes further destruction order is of importance. For these
cases, the XOTcl language provides an exit handler, which is a user-defined proc, which invokes
user-defined exit handling just before the destruction of objects, classes, meta-classes is invoked.
For instance, the exit handler lets the user specify objects which have to be destroyed before all
other objects.
The exit handler is defined as a proc of Object, which is per default empty:
There are some procs of the Object class pre-defined, which let us specify an exit handler
conveniently:
destroys the object aObj before all other objects and prints the message existing to the screen. With
getExitHandler the exit handler can be introspected. E.g. if we just want to append the destruction of
object bObj to an existing exit handler, we use getExitHandler:
Object setExitHandler "[Object getExitHandler]; bObj destroy"
unsetExitHandler deletes the exit handler.
produces
Consider you want to perform a deeper integration of an other extension and XOTcl because you
want to benefit from XOTcl's object system. For instance, you might want to introduce composite TK
widgets (sometimes called mega-widgets) as classes and inherit from these classes. Here, you
have two options: you can change or extend the C code of that other extension to provide XOTcl
classes or objects, or you can write an XOTcl wrapper in Tcl. For the first alternative, there are
some examples provided in the XOTcl distribution. XOTclGdbm provides an OO Tcl interface to the
GDBM database, for instance. XOTclSdbm does the same for SDBM, and the TclExpat wrapper
provides a class-based interface to the TclExpat XML parser.
Consider you do not want to change the C code of a Tcl extension. Then you can write an OO
wrapper in XOTcl for the commands of the other extension. For stateless commands, you can
simply write forwarder methods. If the extension maintains some state, you typically associate the
state handle with an XOTcl parameter, acquire the state in the XOTcl constructor, and align the
XOTcl destructor with the stateful instance.
Consider you want to wrap the Tk button widget. You can acquire the widget in the constructor, and
maintain the widget ID in a parameter. You now can forward invocations to this widget ID (e.g. when
using "pack"), or register command callbacks (like buttonPressed). Note that we let the "self"
command be replaced in the scope of the current method so that TK receives the correct object ID
for the callback. In the destructor we destroy the widget as well (we use "catch" because sometimes
widgets can destroyed by other means as well (e.g. by their parent widget, when a widget/object
hierarchy is destroyed at once).
The "trick" to substitute "self" within the current method scope works for all kinds of command
callbacks. Extensions such as TK, however, often work with bindings to (global) variables as well.
Using global variables is frowned upon in the OO community. Instead you should use instance
variables of objects. As Tcl can only bind to existing namespace variables (and XOTcl acquires the
namespace of an object on demand), you have to make sure that the namespace of an object exists
before binding a variable. That can be done with "requireNamespace":
Note that in the above example we have used to tail of the object ID as ID for the widget. Usually, it
is a good idea to the object name, if possible, for TK (and other extensions) IDs as well. Another
option is to use a autoname to get a unique name for the ID.
Sometimes you want to simply send all invocations, not implemented by XOTcl, to the wrapped
command. Here, it is tedious to write a wrapper for each of these methods. Instead you can use
"unknown" to handle automatic forwarding. Consider you want to wrap TK commands like pack and
replace XOTcl object names with their TK widget ID, so that you can use both IDs synonymously.
You can rename the respective TK commands in the following way:
The XOTcl class handling the ID substitution for the TK command might look as follows: