ArticlePDF Available

Programming with ObjVLisp metaclasses in Smalltalk-80

Authors:
  • LIP6/CNRS - Sorbonne Université

Abstract

This paper discusses the introduction of explicit metaclasses à la Ob the Smalltalk-80 language. The rigidity of Smalltalk metaclass ar motivated this work. Consequently we decided to implement the ObjV into the standard Smalltalk-80 system. The resulting combination Classtalk platform. This platform provides a full-size environment to with class-oriented programming by composing implicit metacla Smalltalk and explicit metaclasses à la ObjVlisp. Obviously, these exper not limited to the Smalltalk world and will be useful to understand the metaclass concept advocated by modern object-oriented languag ObjVlisp and CLOS.
-1-
Programming with ObjVlisp Metaclasses
in Smalltalk-80
Jean-Pierre Briot and Pierre Cointe
Equipe Mixte Rank Xerox France - LITP,
Université Pierre et Marie Curie,
4 place Jussieu, 75005 Paris, France
briot/pc@rxf.ibp.fr.uucp
Published in Proc. of OOPSLA'89,
ACM Sigplan Notices, Vol. 24, No 10, October 1989, pages 419-432
Abstract
This paper discusses the introduction of explicit metaclasses à la ObjVlisp into
the Smalltalk-80 language. The rigidity of Smalltalk metaclass architecture
motivated this work. Consequently we decided to implement the ObjVlisp model
into the standard Smalltalk-80 system. The resulting combination defines the
Classtalk platform. This platform provides a full-size environment to experiment
with class-oriented programming by composing implicit metaclasses à la
Smalltalk and explicit metaclasses à la ObjVlisp. Obviously, these experiments are
not limited to the Smalltalk world and will be useful to understand and practice
the metaclass concept advocated by modern object-oriented languages such as
ObjVlisp and CLOS.
1 Introduction
Uniformity is one of the main advantages of Object-Oriented Programming
[Goldberg&Robson83]. Therefore in the sub-field of class-oriented
increasing number of people claim that classes must be considered as "first class
objects" [Cointe87], i.e. described by true and appropriate classes, called
metaclasses.
1.1 Metaclasses are Useful
It has already been argued that metaclasses are useful both at the user and at
the implementor levels to describe and extend the class architecture.
For the implementor, metaclasses are the means to describe and parametrize
the object system itself, for instance to tailor the implementation
[Cointe&Graube88], describe and extend the language in a circular way
[Bobrow&Kiczales88] [Attardi&al89], and control the execution process
[Malenfant&al89]. In short to describe and control the implementation of objects
at the user's level.
For the user, metaclasses define the class methods, which allow to send
messages to classes, e.g. the messages to create new objects, and the instance
variables at the class level, which allow to parametrize classes [Cointe87].
1.2 Metaclasses in Smalltalk
-2-
Historically, Smalltalk was the first language to introduce metaclasses. At the
implementation level, they define the kernel of the architecture (in Smalltalk-80,
the metaclasses of the Kernel-Classes category) in an object-oriented manner. But at
the user level, metaclasses have been voluntarily hidden from the user. When
the user defines a new class, a new metaclass is automatically created by the
system. This implicit metaclass is anonymous, unsharable and strongly coupled
with its private instance, the class which originated it.
This separation between the implementor-level and user-level results in an
architecture which is not fully uniform, complex, and difficult to understand and
extend. This choice eases the ergonomy of the programmer, but it constrains the
constructs he may achieve, as we will see. Consequently people working in the
field of learnability of object-oriented programming claim that the Smalltalk
metaclasses complicate unnecessarily the model and that they should be
removed or at least highlighted [Borning&OShea87]. Nevertheless, taking the
decision to remove metaclasses can lead to remove classes too, and to define
prototype-based Smalltalk languages [Ungar&Smith87].
1.3 Metaclasses in ObjVlisp & CLOS
Taking the opposite approach, many people have been looking for uniform and
explicit metaclasses. Such systems are Loops, ObjVlisp, CLOS and others. We
proposed the ObjVlisp model [Briot&Cointe87] which supports a simple, clean and
minimal architecture for explicit metaclasses. The Common Lisp Object System
(CLOS) [Bobrow&Kiczales88] has also been designed along such an architecture.
Meanwhile ObjVlisp has the drawbacks of its minimality. It does not own
enough class libraries to allow real-size experiments with end-users. CLOS is a
much richer language but there are currently few implementations and its
programming environment is still under work.
1.4 Motivations & Objectives
A previous study [Cointe88] convinced us that the Smalltalk language was
extensible enough to support another metaclass system. Because Smalltalk-80 is
currently the most complete and flexible object-oriented programming
environment, we decided to introduce the uniform architecture of ObjVlisp
metaclasses into it. This integration must be complete in order to experiment
with (meta)class-oriented programming while still reusing standard Smalltalk-80
class libraries. The resulting system, named Classtalk, provides libraries of
metaclasses which the programmer may combine as bricks to design unlimited
metaclass levels.
1.5 Outline of the Paper
Section-2 discusses the limitations of the Smalltalk-80 architecture, namely the
private class/metaclass module and the non uniform protocol to instantiate
objects. Section-3 shortly reviews how the ObjVlisp and CLOS architectures fill
these gaps. Section-4 discusses two options in order to integrate the ObjVlisp
architecture into Smalltalk-80, and then details one implementation. Section-5
describes how we extend the standard Smalltalk-80 programming environment
to provide a specific one suitable to Classtalk explicit metaclasses. Section-6
-3-
introduces a first library of metaclasses. Section-7 explains how we merge the
Borning&Ingalls' multiple inheritance scheme into Classtalk. Section-8 gives an
example of metaclass combinations. Section-9 discusses the reoccurence of the
class/metaclass module and how to definitely solve this constraint by
implementing uniform creation. Section-10 discusses the new issues raised by
this work before concluding.
2 The Smalltalk-80 Metaclass Arcanes
2.1 Kernel (Meta)Classes
Like ObjVlisp or CLOS, Smalltalk-80 uses a kernel of primitive and explicit
metaclasses in order to self-describe classes. Smalltalk-80 calls them
classes. Class describes standard classes (classes which are not metaclasses), and
Metaclass describes metaclasses. To express the common properties of standard
classes and metaclasses, they are both direct-subclasses of ClassDescription
subclass of Behavior. The inheritance hierarchy of the kernel classes is shown
below. The instance variables, which are here defined at the class level, are
enclosed within ().
Object ()
Behavior (superclass methodDict format subclasses)
ClassDescription (instanceVariables organization)
Metaclass (thisClass)
Class (name classPool sharedPools)
We can check the structural difference between a class and a metaclass. A
metaclass uses the backward pointer thisClass to memorize its private metaclass,
while a class has name, classPool and sharedPools variables.
2.2 User Metaclasses
Besides this primitive kernel architecture, the Smalltalk designers chose to hide
the metaclass architecture from the user and to provide an implicit and
automatic metalevel for standard classes.
When a new class is defined, e.g. class Actor, the system automatically creates a
class/metaclass module. This means that the system first creates a new implicit
metaclass and then instantiates it in order to create the class which will be its
sole instance. Such an implicit metaclass is anonymous and is only reachable by
sending the message class to the class it describes, e.g. Actor class. The browser
connects the definitions of the class and its metaclass through the
switch view of the browser.
The user may define methods at the metaclass level. These methods describe
messages which may be sent to the class itself, and are named class methods
order to extend the structure of standard classes, the user may also define
instance variables at the metaclass level. Nevertheless these variables have no
specific names and are not part of the Smalltalk terminology. They must not be
mistaken for class or pool variables which implement shared variables.
2.3 Rigidity of the Metaclass Architecture
-4-
Being implicitly created by the system, the inheritance and instantiation of
metaclasses should obey to some implicit rules. To provide the same inheritance
rule for class and instance methods, the inheritance hierarchy of metaclasses is
parallel to the inheritance hierarchy of classes. In order to have the same
structure and behavior for all implicit metaclasses, each of them is created as an
instance of the kernel class Metaclass. Smalltalk-80 connects the metaclass
inheritance hierarchy to the class hierarchy by declaring the most general
metaclass, Object class, as a subclass of the kernel class Class.
Object ()
Actor ()
Behavior (superclass methodDict format subclasses)
ClassDescription (instanceVariables organization)
Metaclass (thisClass)
Class (name classPool sharedPools)
Object class ()
Actor class ()
Behavior class ()
ClassDescription class ()
Metaclass class ()
Class class ()
We experienced that the implicit class/metaclass module provides a too much
rigid coupling between a class and its metaclass. This leads to limitations in the
expressiveness of the langage as illustrated by the next example.
2.4 The abstract Class Counter-Example
"Abstract class: a class that specifies protocol, but is not able to fully implement
it; by convention, instances are not created of this kind of classes."
[Goldberg&Robson83]
A simple example of abstract class appears when one tries to modelize complex
numbers as objects. Two models for representation are useful for complex
numbers, namely cartesian and polar coordinates. Therefore we define two
standard classes (which are not abstract classes), respectively Cartesian
implement them. The abstract class Complex factorizes the common behavior, for
instance computing arithmetics. In the inheritance hierarchy figure, instance
methods are enclosed within <>.
Complex () <+ - * / conjugate modulus negated>
Cartesian (x y) <x y rho theta x:y: printOn:>
Polar (rho theta) <x y rho theta rho:theta: printOn:>
The problem is to modelize the general behavior of an abstract class, and more
precisely to ensure the fact that such a class cannot create instances. The obvious
way is to forbid instantiation by redefining the standard class method for
creation (in fact allocation) in order to raise an error. This standard method is
named new and belongs to kernel class Behavior. It should be redefined as a class
method, because it (re)defines the behavior of classes. Because class methods
-5-
(and metaclasses) are implicit, we must define a standard class, named
own this method.
!Abstract class methodsFor: '(forbidden) allocation'!
new
self error: 'no instance, I am an abstract class'! !
Then Complex is defined as a subclass of Abstract:
Object <...>
Abstract <>
Complex <...>
Cartesian <...>
Polar <...>
Behavior<... new ...>
ClassDescription <...>
Metaclass <...>
Class <...>
Object class <...>
Abstract class <new>
Complex class <x:y: rho:theta:>
Cartesian class <x:y:>
Polar class <rho:theta:>
Because Complex is defined as a subclass of Abstract, its metaclass
inherits the redefinition of method new owned by Abstract class. Unfortunately
classes Cartesian and Polar both inherit from Complex. Consequently their
corresponding metaclasses also inherit the forbidden instantiation. Thus they
become abstract classes too and it will be impossible to create any complex
number. Unfortunately the rule for implicit inheritance of metaclasses does not
match our intuition.
A pragmatic solution is to explicitly change the inheritance rule by updating
the instance variable superclass, which specifies the inheritance link. Therefore we
declare the most general metaclass, i.e. Object class, as the new superclass:
Cartesian class superclass: Object class.
Polar class superclass: Object class
This solution works but is ad hoc and not modular (we need to redefine
inheritance for every subclass). The complete solution, given in section-3.2, uses
an explicit control on inheritance and instantiation of classes.
2.5 Non Uniform Creation
Smalltalk provides two primitive methods to allocate objects. These methods,
named new and new: are owned by the kernel class Behavior. Method
objects whose structure is defined by named instance variables (such as
whereas new: allocates objects whose structure is defined by indexed variables
(such as Array). Every object in the system, except rockbottom objects such as
numbers, is created by calling one of these allocators. Consequently allocation of
objects is (almost) uniform. However their initialization is not.
-6-
When an object is allocated, the values associated to its instance variables get
the default initial value nil. In order to initialize these variables, no standard
method is provided, and therefore one needs to define explicitly an initialization
method. For instance we define such a method which initializes Cartesian
!Cartesian methodsFor: 'initializing'!
setX: xValue setY: yValue
x _ xValue.
y _ yValue! !
If we want to combine allocation and initialization into a single message for
creation, we have to define the following class method:
!Cartesian class methodsFor: 'creation'!
x: xValue y: yValue
^self new setX: xValue setY: yValue! !
Such initialization and creation methods are mostly specific to each class,
because their selectors follow the names of the instance variables. In the case of
creating classes, however, because all classes share the same structure (all
instance variables defined or inherited by Class), there is a standard creation
method named subclass:instanceVariablesNames:...category: and owned by
provides the appropriate values to create (and initialize) standard classes.
In summary there is no uniform way of creating objects in Smalltalk-80. Only
allocation of objects is uniform. This is not too much burden in standard
Smalltalk-80 because all classes may be created through the same creation
method. But when we will start to parametrize classes by defining new instance
variables on metaclasses, we will need to define specific initialization and
creation methods to deal with these new instance variables. This limitation will
be touched upon after defining some explicit metaclasses of the Classtalk library.
3 The ObjVlisp & CLOS Alternative
The complete solution to the previous limitations has already been exposed in
[Cointe87]. Classes must be explicitly and uniformly created as instances of some
other classes called metaclasses.
ObjVlisp and CLOS are two systems which propose such an architecture.
ObjVlisp is also minimal in the sense of being self-defined by only two classes
which are the root of the instantiation tree (Class) and the root of the inheritance
tree (Object). Class, being an object, must be itself described by (and must be
instance of) some class. The minimal solution proposed in [Briot&Cointe87]
defines Class as instance of itself. This self-instantiation ensures a complete
uniformity and self-description (reflexivity) of the kernel.
3.1 Explicit Metaclasses
An ObjVlisp metaclass is a class which can have access to the standard
allocation message by owning it or by inheriting it. Class, as the holder of the
standard allocation method allocateInstance, is the first metaclass of the system. In
order to inherit this standard allocator, a new metaclass is always created as a
-7-
subclass of a previous one. As opposed to Smalltalk-80, there is no difference
between classes and metaclasses. Consequently the two metaclasses of Smalltalk-
80 (Class and Metaclass) are merged into one only (Class).
3.2 Abstract Class Revisited
In ObjVlisp, as opposed to Smalltalk-80, there is no implicit link between a
class and its private metaclass. Consequently a same metaclass can be used
(shared) to describe different classes. The ObjVlisp solution to the abstract class
problem is summarized by the following architecture:
Object
Complex
Cartesian
Polar
AbstractClass
10+20i
3 cis 45
8-5i
Class
instance of
There are three steps to this solution. Below are the corresponding definitions
in Classtalk:
• create the new metaclass describing all abstract classes. AbstractClass
instance and a subclass of the first metaclass Class. AbstractClass redefines the
allocation methods new (and new:) in order to raise an error,
Class newName: #AbstractClass
superclass: Class
instanceVariableNames: ''
category: 'Metaclass-Library'!
!AbstractClass methodsFor: '(forbidden) allocation'!
new
self error: 'no instance, I am an abstract class'!
new: n
self error: 'no instance, I am an abstract class'! !
• create a new abstract class Complex, instance of AbstractClass and subclass of
Object,
AbstractClass newName: #Complex
superclass: Object
instanceVariableNames: ''
category: 'Numeric-Complex
-8-
• create the two standard classes Cartesian and Polar as instances of
subclasses of Complex,
Class newName: #Cartesian
superclass: Complex
instanceVariableNames: 'x y '
category: 'Numeric-Complex'!
Class newName: #Polar
superclass: Complex
instanceVariableNames: 'rho theta '
category: 'Numeric-Complex'!
3.3 Uniform Creation
In ObjVlisp and CLOS, creation of objects is uniform. This is achieved by
combination of an allocation and an initialization method:
creation = initialization o allocation
Class holds the standard allocation method, named allocateInstance
standard creation method, named makeInstance:. There are two standard
initialization methods, both of them named initializeInstance:. The first one is owned
by Object and defines standard initialization of objects. The second one, owned by
Class, defines initialization of classes. Initializing classes is more complex and
includes for instance compiling static inheritance of instance variables.
Consequently this second initialization method extends (and calls) the most
general initialization method owned by Object. Following is the inheritance
hierarchy of the ObjVlisp kernel:
Object <initializeInstance:>
Class <allocateInstance initializeInstance: makeInstance:>
Compared with Smalltalk, the ObjVlisp makeInstance: method includes parameters
for object initialization, which it transmits to the initializeInstance: method, whereas
the Smalltalk-80 method new is a simple allocator (equivalent to allocateInstance
not a complete creation method.
4. Classtalk: ObjVlisp in Smalltalk-80
Implementing ObjVlisp in Smalltalk-80 raises two problems:
• introducing an explicit class architecture not limited to an automatic coupling
between a class and its metaclass,
• introducing a unified method of creation which takes into account both the
allocation and the intitialization procedures.
Smalltalk-80 is extensible enough to propose a simple and clean solution to the
first problem. But its somewhat limited syntax makes it difficult to find a fully
satisfactory solution to the second problem (this will be discussed in section-9).
-9-
The resulting system, named Classtalk, provides explicit classes and metaclasses
in Smalltalk-80 and a solution to uniformity of creation. Classtalk is used as a
platform to experiment with (meta)class-oriented programming.
4.1 Creating Classes Explicitly
In order to create a class as an explicit instance of a metaclass we introduce a
new creation message, with selector newName:superclass:instanceVariableNames:category:. Its
parameters come from ObjVlisp, but its keywords retain the Smalltalk-80 syntax
and conventions. Thus we chose as its selector. Note that, as advocated by
ObjVlisp, class and pool variables are suppressed for the sake of simplicity. This
new creation message is sent to the metaclass, i.e. the creator, and not to the
superclass as in standard Smalltalk-80. This follows the principle of creating any
object as an instance of a class.
4.2 Implementation Alternative
The question which remains opened is: "Which metaclass should own this new
method for creating explicit classes?". Because in ObjVlisp the owner of this
method is the root of the kernel, i.e. metaclass Class, this question reformulates
into: "How do we transpose the ObjVlisp kernel into the Smalltalk-80
architecture?". At the implementation level, two answers may be given:
• identifying (merging) the ObjVlisp kernel, classes Class and Object
two corresponding Smalltalk-80 classes,
• grafting ObjVlisp by adding to the Smalltalk-80 kernel a new metaclass,
named Classtalk, defined as a subclass of ClassDescription.
4.2.1 Merging
Class already owns the standard method subclass:instanceVariableNames:...category:
creating standard Smalltalk classes. By identifying the ObjVlisp metaclass
with the Smalltalk-80 kernel class Class, the method newName:...category:
also a method of Class.
Class <subclass:...category: ... newName:...category:>
Object class <...>
Behavior class <...>
ClassDescription class <>
Class class <...>
Class is both the instance and an undirect superclass of its metaclass
This provides an implicit self-description of Class, although, as opposed to
ObjVlisp, Class class is not equal to Class. This solution is minimal.
4.2.2 Grafting
The grafting scheme gives more control on the design of the Classtalk kernel,
but complicates its self-description. Classtalk class is a subclass of
Classtalk. Nevertheless we can change the implicit rule of Smalltalk metaclass
inheritance to make Classtalk class a direct subclass of Classtalk:
Classtalk class superclass: Classtalk
-10-
This splits inheritances of structure and behavior into two different trees:
Object ()
Behavior (superclass methodDict format subclasses)
ClassDescription (instanceVariables organization)
Metaclass (thisClass)
Class (name classPool sharedPools)
ObjectClass ()
Behavior class ()
ClassDescription class ()
Classtalk class ()
Classtalk (name category)
Object <...>
Behavior <... new ...>
ClassDescription <...>
Metaclass <>
Class <... subclass:...category: ...>
Classtalk <newName:...category:>
Classtalk class <>
This solution also allows a more precise definition of Classtalk classes. Unused
instance variables such as classPool and sharedPools are no longer defined. We also
may define new ones, e.g. category which will be useful in section 9-4. Nevertheless
the instance variable name and some methods of Class need to be duplicated in
Classtalk.
In the following of the paper, although both solutions are mostly equivalent, we
choose the first one, i.e. class Classtalk, in order to emphasize the difference with
standard Smalltalk-80.
4.3 Explicit Creation of Classes
The implementation of the method newName:...category: to create Classtalk classes
follows the standard implementation of class creation. It includes a dispatch
along the type of the superclass (with named or indexed variables). Like in
standard Smalltalk-80, the "auxiliary method" newName:environment:...category:
common implementation between classes with named or indexed instance
variables.
To focus on the semantic of these two methods, we give their definitions
without the type dispatcher and without the pieces of code related to the
management of the programming environment (syntax check, changes
management...) which are replaced by comments.
-11-
!Classtalk methodsFor: 'Classtalk - class creation'!
newName: n superclass: s instanceVariableNames: i category: c
"Dispatch along classes with indexed variables."
^self
newName: n
environment: Smalltalk
superclass: s
otherSupers: nil
instanceVariableNames: i
variable: false
words: true
pointers: true
category: c!
newName: n environment: e superclass: s otherSupers: o instanceVariableNames:
variable: v words: w pointers: p category: c
| newClass "..." |
"Syntax checking and redefinition management."
"(1) Allocation of the new class."
newClass _ self new.
"(2) Initialization of the new class - 1."
newClass
superclass: s
methodDict: MethodDictionary new
format: -8192
name: n
organization: ClassOrganizer new
instVarNames: (Scanner new scanFieldNames: i)
classPool: nil
sharedPools: nil.
"(3) Specification of remaining superclasses."
os isNil ifFalse: [newClass otherSupers: os].
"(4) Initialization of the new class - 2."
newClass
format: newClass allInstVarNames size
variable: v
words: w
pointers: p.
"Environment management."
ObjVlispOrganization classify: newClass name
under: categoryString asSymbol.
"Hierarchy updating and change management."
"(5) Compilation of multiple inheritance."
o isNil ifFalse: [newClass copyMethods].
^newClass! !
• as suggested by ObjVlisp a class creation is realized in two stages: allocation
(1) and initialization (2 & 4). The new class created (temporary variable
is defined explicitly as an instance of a previous metaclass: self new
standard Smalltalk-80, the initialization process takes place in two successive
steps: (2) and (4).
• to organize Classtalk classes in a specialized browser we introduce a new
organizer, the global variable ObjVlispOrganization which is coupled with the
Classtalk browser.
-12-
• the method newName:environment:..category: introduces a parameter prefixed by
the keyword otherSupers:. It specifies an unused array of superclasses (calling value
is nil). Meanwhile, this allows this method to be reused when introducing multiple
inheritance (see section-7).
• expressions (3) and (5) are evaluated in the case of multiple inheritance only.
(3) assigns the array of remaining superclasses. (5) calls the management of
multiple inheritance provided by the standard extension of Smalltalk-80
[IngallsBorning82]. This will recompile the methods or generate conflicting
methods when needed.
5 The Classtalk Environment
The Smalltalk-80 standard browser may confuse the programmer when
browsing on Classtalk classes. When the instance/class switch is set to
browser shows the explicit metaclass, and not an implicit one as in standard
Smalltalk-80. Moreover the template and the definition printed in the browser
do not reflect the Classtalk definition. If evaluated, the definition of the class
already created won't lead to the same result. It will redefine the class in the
standard Smalltalk-80 way (with an implicit metaclass) while forgetting about
the previous explicit metaclass.
Therefore we designed a specific browser dedicated to Classtalk classes. The
differences lie in the removal of the instance/class switch and the adjustment of
templates and definitions in order to make clear the Classtalk way of creating
classes.
This browser is also interfaced with a generic tree editor [Wolinski89] in order
to browse onto both the instantiation and the inheritance graphs.
-13-
6 Library of Metaclasses
This new browser was helpful to develop a library of primitive Classtalk
metaclasses. Our idea was to reuse them as bricks to define more complex
metaclasses by combining with both the instantiation and inheritance
mechanisms.
In this section we propose to introduce and comment some of them. Let us
recall that the creation rule for these metaclasses is the following one:
direct-subclass of Classtalk or a direct-subclass of another explicit metaclass.
naming convention is that each metaclass' name ends up with Class
MetaAccessClass
AutoInitClass
AbstractClass
MetaTypedClass
TypedClass
MemoClass
AccessClass
Classtalk
6.1 AbstractClass
This metaclass modelizes abstract classes, i.e. non-instantiable classes, as
defined and used in section-2.4.
6.2 AutoInitClass
-14-
This metaclass modelizes classes which provide their instances with automatic
initialization.
In order to get an automatic initialization of objects, every Smalltalk-80
programmer has at least once redefined the class method new. To make this use
common and to avoid code duplication, we modelize this behavior in the specific
metaclass AutoInitClass. A class instance of AutoInitClass has the following behavior: it
sends the message init to an instance being created.
Classtalk newName: #AutoInitClass
superclass: Classtalk
instanceVariableNames: ''
category: 'Metaclass-Library'!
!AutoInitClass methodsFor: 'allocation'!
new
^super new init! !
6.3 MemoClass
This metaclass modelizes classes which memorize the collection of all their
instances by using an explicit backpointer.
This backpointer is implemented by a new instance variable instances
the metaclass level. Its value is an ordered collection memorizing all the
instances which are created.
This variable needs to be initialized to an empty collection before starting to
create instances. In order to provide an automatic initialization, we define
MemoClass as an instance of AutoInitClass.
AutoInitClass newName: #MemoClass
superclass: Classtalk
instanceVariableNames: 'instances '
category: 'Metaclass-Library'!
!MemoClass methodsFor: 'init'!
init
instances _ OrderedCollection new! !
!MemoClass methodsFor: 'allocation'!
new
"Method add: returns the object added."
^instances add: super new! !
!MemoClass methodsFor: 'accessing'!
instances
^instances! !
6.4 TypedClass
This metaclass modelizes classes which are parametrized by a type [Cointe87].
TypedClass introduces the new instance variable type and two associated accessing
methods. In order to provide an explicit initialization of this variable, we need to
extend and specialize the standard Classtalk message for creating classes. The
new creation method, named newName:...type:category: combines the standard
-15-
newName:...category: creation method with the assignment of the type. Meanwhile,
the definition of this new method led us to introduce the new metaclass
MetaTypedClass whose only goal is to hold this extended creation method. A non
uniform initialization constrains us to reintroduce the class/metaclass module.
Classtalk newName: #MetaTypedClass
superclass: Classtalk
instanceVariableNames: ''
category: 'Metaclass-Library'!
MetaTypedClass newName: #TypedClass
superclass: Classtalk
instanceVariableNames: 'type '
category: 'Metaclass-Library'!
!TypedClass methodsFor: 'accessing'!
type
^type!
type: aClass
type _ aClass! !
!MetaTypedClass methodsFor: 'creation'!
newName: n superclass: s instanceVariableNames: i type: aClass category: c
^(self newName: n superclass: s instanceVariableNames: i category: c) type: aClass! !
6.5 AccessClass
This metaclass modelizes classes which may provide automatic (read-write)
accessors to their instance variables.
Another repetitive programming problem lies in the definition of accessor
methods. Their selectors are usually associated to the instance variables to which
they give access. In order to relieve the programmer from this routine, we
propose the metaclass AccessClass which describes how to generate automatically
such accessors. The programmer can specify which instance variables will be
public (i.e. with accessors) by using the declaration public:.
The following example is the Classtalk solution to the example described in
[Goldberg&Robson83], pages 289-290:
AccessClass newName: #Record
superclass: Object
instanceVariableNames: 'name address '
public: 'name '
category: 'Access-Example'!
Like TypedClass, the specialization of the creation message leads to introduce a
new metaclass, named MetaAccessClass, to own the extended creation method.
This method, named newName:...public:category:, will compose the standard
newName:...category: method with the call of the method to generate accessors. This
method, named makeIvAccessOn:, is owned by AccessClass. A scanner parses the string
specifying public variables into an array which becomes the parameter of the
message:
-16-
Classtalk newName: #MetaAccessClass
superclass: Classtalk
instanceVariableNames: ''
category: 'Metaclass-Library'!
!MetaAccessClass methodsFor: 'creation'!
newName: n superclass: s instanceVariableNames: i public: p category: c
^(self newName: n superclass: s instanceVariableNames: i category: c)
makeIvAccessOn: (Scanner new scanFieldNames: p)! !
MetaAccessClass newName: #AccessClass
superclass: Classtalk
instanceVariableNames: ''
category: 'Metaclass-Library'!
!AccessClass methodsFor: 'access generation'!
makeIvAccessOn: ivNameArray
ivNameArray isNil ifFalse:
[ivNameArray do: [:ivString |
self compile: ivString , '\^' withCRs , ivString
classified: #accessing;
compile: ivString , ': aValue\' withCRs, ivString , ' _ aValue'
classified: #accessing]]! !
7 MultipleInheritance
We described samples of the library of metaclasses. The programmer may use
them and start to combine them by using instantiation and inheritance. In non
trivial cases, simple inheritance may be not enough, for instance to describe
classes which memorize and initialize their instances, by combining (inheriting
from both) MemoClass and AutoInitClass. Therefore we need multiple inheritance.
We introduce such an extension in Classtalk, while reusing most of the standard
Smalltalk-80 extension for multiple inheritance. We will at first shortly review
the Smalltalk-80 extension and then describe how we interface it with Classtalk.
7.1 Multiple Inheritance In Smalltalk-80
The strategy proposed in [Ingalls&Borning82] is to keep the single inheritance
scheme working. In case of multiple inheritance the first superclass continues to
be the standard superclass while the other ones are stored in the metaclass of the
class. These remaining superclasses are referenced by the new instance variable
otherSupers, which is introduced by the kernel class MetaclassForMultipleInheritance:.
When creating a class with multiple superclasses, the methods which cannot be
reached by the standard single inheritance lookup are recompiled into the
method dictionary of the new class. If several methods with a same selector may
be reached, conflicting inherited methods are automatically generated. To solve
the conflicts their bodies need to be explicited by the programmer.
7.2 Multiple Inheritance In Classtalk
When modelizing multiple inheritance in Classtalk we define the instance
variable otherSupers directly at the class level (and no more at the metaclass one).
Consequently we introduce the metaclass MIClass to define this new instance
-17-
variable. As with metaclasses TypedClass and AccessClass, to extend the creation
method we have to introduce a metaclass, named MetaMIClass.
The method to create classes with multiple superclasses is named
newName:superclasses:instanceVariableNames:category:. Its syntax and implementation are
analog to those of the standard Classtalk method
newName:superclass:instanceVariableNames:category: and we do not show its code in this
paper.
8. Example of Metaclass Combination
To emphasize the Classtalk methodology we now develop the parametrized
stacks example. Our goal is to define stacks whose parameter of the
is typechecked. To make the demonstration easier, and to show how we may
reuse standard libraries, we suppose that a class Stack has been previously
defined, e.g. as a subclass of primitive class Array extended with an
Stack can be either a Classtalk class either a Smalltalk-80 class.
The class architecture we want to discuss is summarized by the following figure
and steps:
MIClass
AbstractClass
TypedClass
AbstractTypedClass
TypedStack
IntegerStack
StringStack
IntegerStack(1 2 )
StringStack('ok' )
Stack
• to express the different types of stacks (IntegerStack, StringStack...), each type of
stack is defined as a parametrized class (i.e. an instance of TypedClass
• to express the common behavior (and structure) of typed stacks, we
introduce the abstract class TypedStack,
• to keep consistency between TypedStack and its subclasses (
StringStack...), TypedStack must be also parametrized,
TypedStack having to be both abstract and parametrized, we introduce the
metaclass AbstractTypedClass, subclass of both AbstractClass and TypedClass
therefore instance of MIClass. Conflicting methods, namely new (and
be redirected onto AbstractClass.
The definition of these classes is following:
-18-
MIClass newName: #AbstractTypedClass
superclasses: 'AbstractClass TypedClass '
instanceVariableNames: ''
category: 'Metaclass-Combination'!
!AbstractTypedClass methodsFor: 'conflicting methods'!
new
^self AbstractClass.new! !
AbstractTypedClass newName: #TypedStack
superclass: Stack
instanceVariableNames: ''
category: 'Stack-Collection'!
!TypedStack methodsFor: 'operations'!
push: x
(x isKindOf: self class type)
ifTrue: [super push: x]
ifFalse: [self error: 'wrong type']! !
TypedClass newName: #IntegerStack
superclass: TypedStack
instanceVariableNames: ''
type: Integer
category: 'Stack-Collection'!
9 Class/Metaclass Module vs Uniform Creation
9.1 Classtalk Library Revisited
The Smalltalk-80 class/metaclass module is split by Classtalk into two explicit
components. On the one hand, this allows an unlimited level of metaclasses and
provides the user with more freedom and possibilities as demonstrated by the
metaclasses library. But on the other hand, we need to define (meta)metaclasses
to own extended class creation methods each time we add some new instance
variable, e.g. metaclasses MetaTypedClass and MetaAccessClass.
The class/metaclass module remains necessary when defining extended
creation messages, as in standard Smalltalk-80. But Smalltalk-80 was taking care
of implicitly creating a metaclass to support the class method, whereas in
Classtalk the programmer gets the burden to explicitly define it.
Consequently in order to avoid defining metaclasses in such case, we need to
make creation to become uniform.
9.2 Initialization Synchronization
Another limitation of the standard Smalltalk-80 non-uniform creation scheme
will be illustrated by the following example. Suppose that we want to modelize
classes whose all instance variables are public. Therefore we define
an instance of AutoInitClass and a subclass of AccessClass, and whose
generates accessors on instance variables defined by the class:
-19-
AutoInitClass newName: #PublicClass
superclass: AccessClass
instanceVariableNames: ''
category: 'Metaclass-Library'!
!PublicClass methodsFor: 'init'!
init
self makeIvAccessOn: instanceVariables! !
Unfortunately, this intuitive formulation won't stand when creating public
classes. The method init is called during the process of allocation (method
redefined in AutoInitClass) and before creation and initialization of the class
(method newName:...category:). Consequently instanceVariable will still be unitialized
(value nil) and none accessing method won't be generated.
We may redefine method newName:...category: to call the init method, but this will
call the init method twice (once at allocation time and one at
creation/initialization time). The modular solution would be to simply redefine
the standard and uniform initialization method if it would exist.
9.3 Uniform Creation
ObjVlisp provides uniform initialization, and consequently creation, because the
method initializeInstance: accepts a variable number of arguments to specify the
initial values of the instance variables. Unfortunately Smalltalk-80 syntax does
not allow selectors with variable arity. Therefore we need to group the
arguments into a single one, some data structure, for instance an array. The
creation of an instance of a cartesian complex would look like:
Cartesian create: #(y 2 x 1)
This reproduces the strategy of CommonLisp-like keywords which may be
reordered at wish, as opposed to explicit and ordered keywords in Smalltalk-80.
9.4 Architecture
The uniform creation method is simply defined as the composition of the
standard allocation method (basicNew) and the uniform initialization method,
named initialize:. This method is defined in kernel class Behavior in order to be
useable by all classes, standard Smalltalk-80 or Classtalk ones:
!Behavior methodsFor: 'creation'!
create: initArray
^self basicNew initialize: initArray! !
Initialization of objects is made generic by defining two methods, one for
initializing classes and owned by class Classtalk, and the other one initializing
terminal objects, and owned by class Object. The initialization of classes is a
specialization of initialization of general objects (use of pseudo-variable
-20-
!Classtalk methodsFor: 'initialization'!
initialize: initArray
super initialize initArray.
self environment: Smalltalk
variable: false
words: true
pointers: true
category: category! !
The method environment:...category: is defined as equivalent to method
newName:environment:...category:, and manages initialization of the class. We suppose
that category is defined as instance variable of class Classtalk, in order to transmit its
value through the initialization process.
9.5 Implementation
There are several alternatives to implement the general initialization method,
owned by Object. The main problem is to evaluate the arguments associated to
instance variables.
One solution is to extend Smalltalk-80 syntax in order to support dynamic
creation of arrays, by using some macro-method or macro-character analog to
Lisp's backquote (`).
We propose here another solution by evaluating the arguments through explicit
calls to the compiler. For each instance variable, the standard method
realizes the binding, by using method indexOf:ifAbsent: to find the index of the
variable, and explicitly calling the compiler to compute the initial value:
!Object methodsFor: 'initialize-release'!
initialize: initArray
| i max ivNames context |
initArray isNil ifFalse:
[i _ 1.
max _ initArray size.
ivNames _ self class allInstVarNames.
context _ thisContext sender sender.
[i < max] whileTrue:
[self instVarAt: (ivNames indexOf: (initArray at: i) ifAbsent:
[self error: 'unknown instance variable: ' , (initArray at: i) printString])
put: (Compiler new
evaluate: (initArray at: i+1) printString
in: context
to: context receiver
notifying: self
ifFail: [self error: 'compilation of initialize failed']).
i _ i+2]]! !
9.6 Examples
We will now redefine the metaclass TypedClass and its instance IntegerStack
show this simplification. Note that defining MetaTypedClass is now no more
necessary, because no specific creation method needs to be defined:
-21-
Classtalk create: #(
name #TypedClass
superclass Classtalk
instanceVariables'type'
category 'Metaclass-Library')!
TypedClass create: #(
name #IntegerStack
superclass TypedStack
instanceVariables''
type Integer
category 'Stack-Collection)!
The good definition of PublicClass is by redefining the initialize: method. Because
initialization is now uniform, the metaclass AutoInitClass is no more necessary and
may be removed from the library.
Classtalk create: #(
name #PublicClass
superclass AccessClass
instanceVariables''
category 'Metaclass-Library')!
!PublicClass methodsFor: 'init'!
initialize: initArray
super initialize: initArray.
self makeIvAccessOn: instanceVariables! !
10 Future Work & Conclusion
Before concluding, we now review the current limitations or weaknesses of the
Classtalk platform and methodology.
Methodology: when designing non trivial constructions, the programmer
needs to define new specific metaclasses which will model specific behaviors and
contribute to ultimately create the bottom-end objects that he wishes to describe.
Because of the class abstraction metaphor, the programmer needs to define
classes before creating its concrete instances. When using extensively
metaclasses on multiple levels, this leads the programmer to go up several levels
to define the root of his application architecture before progressively
instantiating down to bottom-end instances. This unfortunately breaks down a
little bit the interactive and incremental programming philosophy of Smalltalk-
80.
Some practical consequence is also about example methods. Convention in
Smalltalk-80 is to define them as class methods. In Classtalk a metaclass may be
shared by several classes, consequently if defining specific (non-shareable)
examples, the programmer needs to define some specific metaclass.
In order to alleviate these problems, we are currently studying how the
interactive programming environment could help the programmer along the
design of multi-levels constructions.
Uniformity of Creation: We implemented a prototype of uniform creation
and validated it by redefining all Classtalk library metaclasses and examples.
-22-
However we find the explicit calls to the compiler too much heavy solution. We
will now evaluate some minimal syntactic extension to support creation of arrays
with evaluation of their elements.
Class/Metaclass Compatibility: Defining explicit metaclasses raises the
issue of compatibility between a class and its metaclass, i.e. the mutual
hypotheses about the methods they define [Graube89]. This may lead to non-
transparent problems when reusing standard Smalltalk-80 classes. For example,
suppose that we define the class Stack as a subclass of class OrderedCollection
than class Array. OrderedCollection defines some private initialization method, named
setIndices, to properly initialize the indices. The allocation method of OrderedCollection
class is redefined in order to automatically ensure the initialization:
!OrderedCollection class methodsFor: 'instance creation'!
new: anInteger
^(super new: anInteger) setIndices! !
If the metaclass of typed stacks, i.e. metaclass AbstractTypedClass (or
does not provide such redefinition, stacks won't be properly initialized, and
cannot be used.
Smalltalk-80 ensures automatic compatibility between a class and its
metaclass, thanks to the rule for parallel inheritance hierarchies. By splitting the
Smalltalk-80 implicit class/metaclass module, we leave this responsability to the
programmer. In order to help him when defining classes, we will extend the
Classtalk programming environment in order to automatically check for such
incompatibilities by browsing through the hierarchies.
(No) Method Combination: The example of typed stacks may be further
extended and complexified by adding the memorization ability to typed stacks.
By inheriting both from AutoInitClass and MemoClass, we encounter some
combination problem. Choosing the right method new to solve the conflict is not
enough, we need a real combination of the two inherited behaviors.
Unfortunately such ability for method combination is not available in the
standard Smalltalk-80 extension for multiple inheritance that we are reusing.
Therefore we plan to design a much richer extension for multiple inheritance to
provides real combination of methods in the spirit of CLOS.
Further Work: Besides solving limitations of the current system, we started to
use the Classtalk platform for intensive experiment with the programming with
metaclasses methodology, and to apply it to simulate part-whole hierarchy,
parametrization..., by extending the current library of metaclasses with new
elements and new combinations.
Conclusion: Although a starting project, this realization already demonstrated
once more that Smalltalk-80 is an extensible system, and that the ObjVlisp
architecture is general enough to be applied to Smalltalk.
Acknowledgements: We thank Nicolas Graube, François Pachet and Jean-
François Perrot for discussions about the project, and Francis Wolinski for
providing his generic tree editor which is used in the Classtalk environment.
-23-
11 Bibliography
[Attardi&al89] G. Attardi, C. Bonini, M. Boscotrecase, T. Flagella and M. Gaspari,
Metalevel Programming in CLOS, ECOOP'89, July 1989.
[Bobrow&Kiczales88] D.G. Bobrow and G. Kiczales, The Common Lisp Object System
Metaobject Kernel - A Status Report, ACM Conference on Lisp and Functional
Programming (LFP'88), pages 309-315, July 1988.
[Borning&OShea87] A. Borning and T. O'Shea, Deltatalk: An Empirically and
Aesthetically Motivated Simplification of the Smalltalk-80 Language
LNCS, No 276, pages 1-10, Springer-Verlag, June 1987.
[Briot&Cointe87] J.-P. Briot and P. Cointe, A Uniform Model for Object-Oriented
Languages Using The Class Abstraction, IJCAI'87, Vol. 1, pages 40-43, August
1987.
[Cointe87] P. Cointe, Metaclasses are First Class: the ObjVlisp Model
pages 156-167.
[Cointe&Graube88] P. Cointe and N. Graube, Programming with Metaclasses in
CLOS, First CLOS Users and Implementors Workshop, Xerox Parc, Palo Alto CA,
USA, October 1988.
[Cointe88] P. Cointe, A Tutorial Introduction to Metaclass Architectures as Provided
by Class Oriented Languages, International Conference on Fifth Generation
Computer Systems (FGCS'88), Vol. 2, pages 592-608, November-December 1988.
[Goldberg&Robson83] A. Goldberg and D. Robson, Smalltalk-80: the Language and
its Implementation, Series in Computer Science, Addison Wesley, 1983.
[Graube89] N. Graube, Metaclass Compatibility, same volume.
[Ingalls&Borning82] D.H.H. Ingalls and A.H. Borning, Multiple Inheritance in
Smalltalk-80, Proceedings of the National Conference on Artificial Intelligence,
pages 234-237, August 1982.
[Malenfant&al89] Malenfant, G. Lapalme and J. Vaucher, ObjVProlog: Metaclasses
in Logic, ECOOP'89, July 1989.
[Ungar&Smith87] D. Ungar and R.B. Smith, Self: The Power of Simplicity
OOPSLA'87, pages 227-242.
[Wolinski89] F. Wolinski, Le Système MV2C: Modélisation et Génération
d'Interfaces Homme-Machine, Report 89/38, Laforia, Université Pierre et Marie
Curie, Paris, April 1989.
... Les besoins croissants des applications pour prendre en compte la distribution des données [199], leur stockage ou encore leur évolution [42], m'ont rapidement donné la certitude que les services à ajouter allaient être de plus en plus nombreux et qu'il était donc très important d'ouvrir les langages afin de leur permettre de modifier leur comportement. Les principales approches que l'on retrouve dans la littérature reposent sur un protocole métaobjet, sur la réflexité et plus généralement sur l'identification d'un métaniveau dans les langages [145, 70, 54]. On verra ci-dessous comment cette constatation nous a conduits à la réalisation du modèle OFL dont l'objectif est la modélisation des langages à objets [93]. ...
... Métaprogrammation. La métaprogrammation est née du besoin d'ouverture des langages ; les protocoles à métaobjets [169] formalisent le comportement d'un langage à objets à l'intérieur de métaobjets et implémentent sa sémantique [80, 54, 83, 82]. Ces métaobjets sont associés à une ou plusieurs classes qu'ils décrivent de manière fonctionnelle et structurelle et un métalangage qui peut être vu comme l'extension d'un langage à objets, est nécessaire pour les exprimer. ...
Article
Ce mémoire a pour objectif de donner un aperçu précis et synthétique des activités de recherches que j'ai développées depuis le début de ma carrière d'enseignant-chercheur. Le travail qui va vous être présenté n'est pas le travail d'une seule personne mais le résultat de la collaboration avec plusieurs personnes que j'ai encadrées ou co-encadrées. Ce petit bout de vie dans la recherche doit bien sûr aussi beaucoup aux autres chercheurs ; je pense bien sûr à ceux du projet OCL et en particulier à Roger Rousseau mais aussi à tous ceux que j'ai rencontrés et côtoyés dans le laboratoire ou dans les divers congrès. Vous l'aurez donc compris toutes les idées présentées ci-après ne sont pas issues d'une seule personne, mais je revendique la démarche qui les a guidée. Je me suis intéressé dès mon stage de DEA aux problèmes liés à la conception de logiciels et en particulier aux aspects concernant la réutilisation, la fiabilité et l'évolution des applications. Ce document retrace mes contributions dans ce domaine.
Article
Full-text available
Abstract The MusES system,is intended,to provide,an explicit representation,of musical knowledge,involved,in tonal music chord sequences,analysis. We describe in this paper the first layer of the system, which provides an operational representation of pitch classes and their algebra, as well as standard calculus on scales, intervals and chords. The proposed representation takes enharmonic spelling into account, i.e differentiates between,equivalent,pitch-classes (e.g. C# and Db). This first layer is intended,to provide a solid foundation for musical symbolic knowledge-based systems. As such, it provides,an ontology,to describe the basic units of harmony. This first layer of the MusES system,may,also be used as a pedagogical,example,for those wishing,to apply object-oriented techniques,to musical knowledge,representation. Résumé Le système MusES a comme,objectif de représenter,les connaissances,musicales nécessaires à l'analyse harmonique,de séquences d'accords en musique,tonale. Nous décrivons,ici la première,couche,du système qui propose une représentation opérationnelle des notes et de leur algèbre, ainsi que des intervalles, gammes et accords. Cette représentation,a comme,particularité de prendre,en compte,les problèmes d'enharmonie, i.e. de différencier les notes équivalentes comme Do# et Réb. Cette première couche,est utilisée pour,l'étude de mécanismes,d'analyse harmonique,et peut être considérée comme,une ontologie,des concepts,de base de l'harmonie. Le but de ce document,est aussi de proposer,un exemple,non,trivial d'application,de Smalltalk-80 à l'usage des musiciens,désirant se lancer dans,la programmation,par objets. An object-oriented representation of pitch-classes, intervals, scales and chords 2
Article
Introduction Ce m'emoire se propose de rappeler mon activit'e de recherche entre les ann'ees 1982 et 1989. L'ensemble de ces travaux porte sur l'analyse des m'ecanismes fondamentaux des langages `a objets. Les langages `a objets sont une famille de langages de programmation rest'ee tr`es active depuis la fin des ann'ees 70. Le style de programmation associ'e, appel'e programmation par objets, se caract'erise par la description du probl`eme `a mod'eliser sous la forme d'un grand nombre d'entit'es autonomes (appel'ees objets) et de leurs protocoles d'interaction (appel'es messages). La programmation par objets permet au concepteur d'exprimer les concepts mis en jeu directement dans le langage. Ce n'est pas un hasard si un domaine d'application privil'egi'e est la simulation, o`u les objets physiques `a mod'eliser seront sp'ecifi'es et repr'esent'es en tant que tels au niveau du langage [Lieberman 84]. La simulation
Article
Full-text available
We introduce spying, a novel way of programming with objects, based on capsule programming and reflective facilities. This programming style allows easy building of monitoring systems, such as tracers, debuggers. We point out three main problems related to this programming style, and propose practical solutions to some of them. We exemplify our claims with a system that performs master/slave communication across different Smalltalk images. We conclude by proposing a typology of applications where the spying paradigm may be productively used. Key-words: Capsules, Proxies, Monitoring systems, Reflexion. 1. Introduction: a state of the art in capsule programming ? Back in 1986, Pascoe introduced encapsulators [Pascoe 86], a paradigm for object-oriented programming that allowed better structuring of programs. Encapsulators, also called capsules or interceptors [Lalonde&Pugh 91], are objects that "wrap" around arbitrary objects, and redefine some of their behavior in a non intrusive way...
Conference Paper
Full-text available
This paper shows how an attempt at a uniform and reflective definition resulted in an open-ended system supporting ObjVlisp, which we use to simulate object-oriented language extensions. We propose to unify Smalltalk classes and their terminal instances. This unification allows us to treat a class as a “first class citizen”, to give a circular definition of the first metaclass, to access to the metaclass level and finally to control the instantiation link. Because each object is an instance of another one and because a metaclass is a real class inheriting from another one, the metaclass links can be created indefinitely. This uniformity allows us to define the class variables at the metalevel thus suppressing the Smalltalk-80 ambiguity between class variables and instance variables: in our model the instance variables of a class are the class variables of its instances.
Conference Paper
The Smalltalk-80 system offers a language with a small and elegant conceptual core, and a highly interactive programming environment. We believe, however, that it could be made more learnable and usable by a relatively small set of changes. In this paper, we present the results of a series of empirical studies on learnability, and also some informal studies of large implementation projects. Based on these studies, we suggest a number of changes to the Smalltalk-80 language and system.
Le Système MV 2 C: Modélisation et Gé d'Interfaces Homme-Machine
[Wolinski89] F. Wolinski, Le Système MV 2 C: Modélisation et Gé d'Interfaces Homme-Machine, Report 89/38, Laforia, Université Pier Curie, Paris, April 1989.