The scheme package Object Model
1 - Basic ideas.
The object model of the scheme package is defined as follow:
-
all classes of the model, including the predefined class <object>,
are instances of the predefined class <class>,
-
the predefined class <object> is the parent of all defined
classes, including <class> and itself,
-
each class has one and only one direct parent,
-
all user defined objects are instances of the class <object>,
-
each object has an internal environment that contains all the fields (inherited
or not) of his class and their values,
-
objects (including classes) of the system are Scheme syntactic objects
(they can be called like procedures but there arguments are not evaluated
by the kernel),
-
when an object is called, the first parameter must be a symbol mapped with
a method or a syntactic method
in the environment this object's class,
-
methods are closures with a first hidden parameter that can be referred
in the body of the method with the symbol this,
-
methods are defined by the procedure method that as the same syntax
as lambda,
-
methods can be called in a closure-like way but with an extra parameter
(it must be an object) in first position,
-
when a method is called, the environment of this object
is added to the method's one,
-
when a parameter of a method has the same name as a field of this
, the name refers to the parameter,
-
syntactic methods are to methods
what syntaxes are to procedures.
2 - Predefined methods.
1 - Methods of the class <class>.
(class define symbol object)
(class define (symbol ...) ...) |
The first form creates in the environment of 'class' a new mapping
of 'symbol' with the object 'object'. 'symbol' must not be quoted.
The second form creates a method. The parameters are the same as for
a procedure definition with define.
Returns an undefined value. |
(class get symbol) |
The symbol 'symbol' must not be quoted.
Returns the value of the object named 'symbol' in the environement
of 'class' |
(class get-parent) |
Returns the parent class of the class 'class'. It returns <object>
applied on <class> and <object>. |
(class make)
(<class> make class (...)) |
The first form applies to all classes of the system except the class
<class> , it creates an instance the class 'class'.
The fields are initialised to an undefined value.
The second form create a new class that is a child of the class 'class'. |
2 - Methods of the class <object>.
(object get-class) |
Returns the class of the object 'object'. It returns <class>
applied on <object> and <class>. |
(object super symbol ...) |
Calls the first precursor method of the method named 'symbol' in the
inheritance tree of the object 'object'. |
3 - Predefined procedures.
(object? obj) |
Returns #t if 'obj' is an instance of object, #f otherwise. |
(method? obj) |
Returns #t if 'obj' is a method, #f otherwise. Note that if an
object is a method he is also a procedure. |
(method L exp ...) |
Creates a method. It has the same syntax as lambda but
returns a method. |
4 - Example.
In the following example we define a class <list> that encapsulate some
of the functions that applies to Scheme lists.
Notation: The convention is the to write the name of a class
beetween '<' and '>', and to put a dot at the beginning of the name
of a field. It is just a convention: every symbol can be used.
First, we define the class <list> with fields
.head and .size.
(define <list> (<class> make (.head .size)))
Now we can create a list by writing:
(define L (<list> make))
The following statement creates an initialization
method. It takes zero or one argument. The argument must be a list.
We can see in this example that fields of the
list are variables of the method's current environment.
(<list> define (init . L)
(cond ((null? L)
(set! .head '())
(set! .size 0))
(else
(set! .head (car
L))
(set! .size (length
L)))))
Here is the initialization of our list:
(L init '(1 3 2 4 1))
We can define a method that displays a readable
representation of a list. Note that there is no ambiguity in the body of
the method when we call procedure display since methods are defined as
a field of the class of the current object.
(<list> define (display)
(display this))
An other way to do that is:
(<list> define display
(method ()
(display this)))
To display our list we can write:
(L display) which produces the output:
(1 3 2 4 1)
The next three methods respectively adds an element
at the beginning of the list, remove an element and returns the length
of the list.
(<list> define (add obj)
(set! .list (cons obj .list)
(set! .size (+ .size 1)))
(<list> define (remove)
(if (not (null? .head))
(begin
(set! .head (cdr .head))
(set! .size (- .size
1)))))
(<list> define (length)
.size)
This method definition demonstrates an important
aspect of the method invocation: 'this' must be used to call the
methods of the current object.
It also shows that I can write very inefficient
methods ;-).
(<list> define (occurences-of obj)
(if (= (this length) 0)
0
(let ((next ((this get-class) make)))
(next init (cdr .head))
(if (equal? obj (car
.head))
(+ 1 (next occurences-of obj))
(next occurences-of obj)))))
Methods can be called in a functional way: the
first statement gets the value of the field 'number-of' of the class <list>,
the next shows how to call this method in a functional way on our previously
created list and the two last demonstrate that methods can be used by a
function that operate on procedures.
(define meth (<list> get occurences-of))
(meth L 1)
;; --> 2
(apply meth L '(1)) ;; --> 2
(map meth `((,L 1))) ;; --> 2
Stéphane Hillion - 1998