Here, for its pedagogical value, we build a simple object system in Scheme. There are a number of pre-existing Scheme object systems; some are discussed in research papers, others will be found in the various Scheme implementations.
An Object is a combination of:
A common syntax for accessing behaviour of an Object is:
object.method(a1, a2, ..., an)
The common semantics, ignoring inheritance, delegation and overloading, is to choose the behaviour based on a combination of:
a1, ..., an
Commonly, languages are designed so that the behaviour for a type of object only needs to be specified
when introducing that type; when calling a method a selection based first on object type happens automatically.
Some languages also allow the method to access any priviledged / internal / hidden / private state of the object,
but aside from this the object can be viewed as another (non-null, non-primitive) argument.
In fact in Java and C++, the object is available in the method as an implicit extra argument 'this' and
in Python the method is declared with an explicit extra parameter for the object.
The method name can also be viewed as an argument (Smalltalk might be an example whose syntax blurs the distinction),
though often restricted to a literal string-like value from a restricted character set.
We will model an object as a function whose behaviours are invoked by passing the method name and arguments. Our Scheme syntax for this will be:
(object 'method a1 a2 ... an)
Let's make an object with an internal count that can be incremented, and retrieved.
More specifically, the internal state is a count starting at 0, and there are
two methods called increment and getCount that increment and return the count respectively.
For (momentary) simplicity, the methods have no arguments.
(define obj
(let ((count 0))
(lambda (method-name) (cond ((equal? method-name 'getCount) count)
((equal? method-name 'increment) (set! count (+ count 1)))))))
(obj 'getCount) ; => 0
(obj 'increment)
(obj 'getCount) ; => 1
(obj 'increment)
(obj 'increment)
(obj 'getCount) ; => 3
In a language with classes, a class is a factory containing a blueprint / template / recipe
for objects with the same kind of state and behaviour (but independent values of the state!).
The syntax for creating an instance of a class called Counter would be something like:
new Counter()
Ignoring the syntax, this simply depends on Counter, and can be thought of as `calling' Counter.
So this is what we will do in Scheme: make a function that when called returns one of our object-like functions:
(define Counter
(lambda ()
(let ((count 0))
(lambda (method-name) (cond ((equal? method-name 'getCount) count)
((equal? method-name 'increment) (set! count (+ count 1))))))))
(define o1 (Counter))
(o1 'getCount) ; => 0
(o1 'increment) ; => 1
(define o2 (Counter))
(o2 'getCount) ; => 0
(o1 'getCount) ; => 1
If we make a lot of classes, we at least want to get rid of the repetition:
(lambda (method-name) (cond ((equal? method-name '
((equal? method-name '
We would like to say something like:
(methods (getCount count)
(increment (set! count (+ count 1))))
In general, we would like
(methods (name implementation)
...)
to mean
(lambda (method-name) (cond ((equal? 'name) implementation)
...))
The beauty of Scheme is that we can say exactly this!
(define-syntax methods ; define a code transformer named "methods"
(syntax-rules () ; the simplest specification language for code transformation
((methods (name implementation) ; pattern to match
...) ;
(lambda (method-name) (cond ((equal? 'name) implementation) ; code to generate
...))))) ;
(define Counter
(lambda ()
(let ((count 0))
(methods (getCount count)
(increment (set! count (+ count 1)))))))
Of course further syntax can be defined to make this even nicer.
As an exercise, define a syntax called class and one called ++
that lets us define Counter by:
(class Counter
((count 0)) ; list of pairs of variable name and initial value (only one pair for Counter)
((getCount count) ; list of pairs of method name and implementation
(increment (++ count)))) ;
If we make a lot of getter and setter methods, that can be automated as well.
It's possible (using the keyword feature of define-syntax, not shown above)
to define class so that all we need to write is:
(class Counter
((count 0 getter))
((increment (++ count))))
Compare this with the Java version:
class Counter {
private int count;
public void increment() { ++count; }
public int getCount() { return count; }
}
Build the object-oriented system you always wanted!