A class can be understood as a template, from which it is possible to create objects, which are instances of the class. The following pattern outlines how a class can be simulated via a procedure definition in Scheme.
(define (class-name)
(let ((instance-var init-value)
...)
(define (method parameter-list)
method-body)
...
(define (self message)
(cond ((eqv? message selector) method)
...
(else (error "Undefined message" message))))
self))
Here and in the following I use an ellipsis ... to indicate that the construct in front of the dots can occur an arbitrary number of times (including zero times). Elements emphasized using the italic font are considered as variables in the presented context. The elements shown in normal fonting are regarded as constant.
The name of the class is class-name.
The let
construct binds a number of instance variables to
their initial values.
In the scope of the instance variables a number of methods
are defined. Each method is a procedure in the implementation
language.
Self
implements the so-called method lookup procedure.
Self
takes a message as a parameter, and it represents a table
that maps method selectors to the actual methods.
A message may be any type of object, for instance symbols,
that can be discriminated by eqv?
on their natural denotation.
Self
is returned from the procedure that simulates the class definition.
Without loss of generality I will assume that procedures that simulate
classes are parameter less. (Possible parameters play the role
of instance variables, and consequently, they can be placed
together with the other instance variables in the let
construct.)
The class can be instantiated by calling the procedure, which represents the class:
(define instance (class-name))
However, I prefer to embed the instantiation into a primitive, which I call new-instance :
(define (new-instance class) (class))
Using this primitive, the instantiation of class-name
from above can be written in the following way:
(define instance (new-instance class-name))
Following this definition, instance is a reference to a new object of class class-name . At the implementation level, instance refers to the procedure self . Self
directly holds on to the operations of the class, and indirectly to the local state of the object. One can think of self as identifying the object, and as a handle to the object from the ``outside world''.
Using the class template from above, it is not possible to bypass the
method interface in reading or mutating the state of an object. This
may be felt as a natural limitation, because it protects the state, as
does an abstract datatype. Following the simulation-oriented and
experimental perspective of this report it is, however, unfortunate to
enforce the limited access to the instance variables. It is, of course,
possible to define reader and writer methods that can access and mutate
the state, but it is tedious to do so
for every instance variable.