Oliver Laumann
The OOPS package provides a minimal set of tools that enables
a Scheme programmer to program in an object oriented style.
The functionality of OOPS is similar to that of packages like
CLOS and SCOOPS, although the current version does
not support multiple inheritance.
The rest of this memo serves as a reference guide to the
OOPS package; the reader is assumed to be familiar with
the terminology of object oriented programming.
Programs that make use of the OOPS package should include the line
(require 'oops)
New classes are defined by means of the define-class macro. The syntax of define-class is
(define-class class-name . options)
(super-class class-name)
(class-vars . var-specs)
(instance-vars . var-specs)
(symbol initializer).
(define-class foo (class-vars (a 10) (b (* a 2))))
A class inherits all class variables, instance variables, and methods of its super-class. When a class and its super-class each have an instance variable with the same name, the corresponding var-specs must either both have no initializer or initializers with the same value, otherwise an ``initializer mismatch'' error is signaled by define-class.
Each instance of a class has an instance variable named self. The value of self is the instance with respect to which self is evaluated. self can be used by methods as the argument to send (see below) to invoke another method within the current instance.
define-class
does not have a meaningful return value,
instead it has a side-effect on the environment in which it
is invoked.
The macro make-instance is used to create an instance of a class; it returns the instance as its value. The syntax is
(make-instance class . args)
(symbol initializer)
make-instance initializes the newly created instance by invoking the initialize-instance method for the class and all super-classes in super-class to sub-class order. That is, the initialize-instance method of the class specified in the call to make-instance is called after all other initialize-instance methods. The arguments passed to the initialize-instance method of a class are those arguments of the call to make-instance that do not represent an initialization form for an instance variable. These arguments are evaluated in the current environment. It is not required for a class to have an initialize-instance method.
Consider the following example:
(require 'oops) (define-class c (instance-vars a)) (define-class d (instance-vars (b 10)) (super-class c)) (define-method c (initialize-instance . args) (print (cons 'c args))) (define-method d (initialize-instance . args) (print (cons 'd args)))
(define x 99) (define i (make-instance d (a 20) 'foo (b x) x))
(c foo 99) (d foo 99)
Note that first the
initialize-instance
method of
c
is invoked and then that of the class
d.
The instance variables
a
and
b
would be initialized to 20 and 99, respectively.
A new method can be defined by means of the define-method macro. The syntax is
(define-method class lambda-list . body)
define-method
simply creates a class-variable with the name
of the method, creates a lambda closure using the lambda-list
and the body forms, and binds the resulting procedure to
the newly-created variable.
When a message with the name of the method is sent to an instance
of the class, the method is invoked, and the body is evaluated
in the scope of the instance (so that it can access all instance
and class variables).
A message can be sent to an instance by means of the function send. The syntax of send is
(send instance message . args)
(define-class c (instance-vars a) (class-vars (b 10))) (define-method c (foo x) (cons (set! a x) b)) ; set! returns previous value (define i (make-instance c (a 99))) (send i 'foo 1) returns (99 . 10) (send i 'foo 2) returns (1 . 10)
When a message is sent to an instance for which no method has been defined, a ``message not understood'' error is signaled.
The function
send-if-handles
is identical to
send,
except that it returns a list of one element, the return value
of the method, or
#f
when the message is not understood by the instance.
The macro with-instance can be used to evaluate expressions within the scope of an instance. The syntax is
(with-instance instance . body).
(define-class c (class-vars (x 5)) (instance-vars y)) (define i (make-instance c (y 1))) (define x 10) (with-instance i (cons x y)) returns (5 . 1)
Generally class and instance variables are manipulated by methods or, if applicable, from within a with-instance form. In addition, values can be assigned to class and instance variables without involving a message send by means of the instance-set! macro. The syntax of instance-set! is
(instance-set! instance variable value)
Class variables can be modified without involving an instance of the class by means of the macro class-set!:
(class-set! class variable value).
(instance-set! i 'var x)
(with-instance i (set! var x))
The function class-name returns the name of a class (a symbol) or, when applied to an instance, the name of the class of which it is an instance.
The predicate method-known? can be used to check whether a method of a given name is known to a class. The syntax is
(method-known? method class)
The type predicates class? and instance? can be used to check whether an object is a class or an instance, respectively.
The functions
(check-class symbol object)
(check-instance symbol object)
The functions describe-class and describe-instance print the components (name, class/instance variables, etc.) of a class or instance, respectively. The function describe has been extended in way that when (feature? 'oops) is true, describe-class or describe-instance are called when describe is applied to an object that satisfies class? or instance?, respectively.