chickadee » operations » object

(object procedure . method-clauses)syntax

Returns: object

An OBJECT-expression yields an object which is prepared to handle generic operations according to the method-clauses. In the following description, ``the object'' refers to the value of a given OBJECT-expression.

Each method-clause should be of the form

 ((operation . variables) . body)

Operation is an evaluated position, and is typically a variable which evaluates to an operation, although it may be any expression. When an operation is called with the object as its first argument, the operation-expressions are evaluated, and if one yields the operation being applied to the object, the corresponding method-clause is selected. The operation is then performed according to the selected method-clause: the clause's variables are bound to the arguments to the operation, and its body, an implicit block, is evaluated.

(define op (operation #f))
(op (object #f ((op self) 34)))      ==> 34
(op (object #f ((op self x) x)) 55)  ==> 55

Procedure may be any expression, and is evaluated at the time the OBJECT-expression is evaluated. The object, when called, simply calls the value of the procedure expression, passing on any arguments. Typically procedure might be either a LAMBDA-expression, if the object is to be callable, or it is #f, which by convention means that the object is not intended to be called, the value of #f being an uncallable object.

In the degenerate case, where there are no method clauses, the value of

 (object (lambda args . body))

is indistinguishable from that of

 (lambda args . body)

The semantics of the OBJECT and OPERATION special forms can be described in terms of hypothetical primitive procedures *OBJECT and GET-HANDLER. These primitives do not actually exist, but are introduced here as expository aids. *OBJECT takes two arguments, and returns an object which, when called, calls the object which was *OBJECT's first argument, and when given to GET-HANDLER returns the object which was *OBJECT's second argument. That is, *OBJECT creates a two-component record (like a pair), GET-HANDLER extracts one component, and the other component is called when the record is called.

 (get-handler (*object proc handler))   ==>  handler
 ((*object proc handler) arg ...)       ==>  (proc arg ...)

In addition, GET-HANDLER is defined on all objects to return some handler, even objects not created by *OBJECT (if indeed there are any such objects).

Given these primitives, the following rough equivalence holds:

 (object proc
       ((op1 . args1) . body1)
       ((op2 . args2) . body2)
       ...
       ((opn . argsn) . bodyn))
 ==>
 (*object proc
        (lambda (op)
          (switch op
            (op1 (lambda args1 . body1)) 
            (op2 (lambda args2 . body2)) 
            ...
            (opn (lambda argsn . bodyn))
            (else #f))))

The outer LAMBDA-expression yields the object's handler; the inner LAMBDA-expressions yield the methods, and the mapping from operations to methods is accomplished by the SWITCH-expression Note that the syntactic positions

 op1,
 op2,
 ...
 opN

are evaluated positions, and the operation expressions are evaluated when an operation is applied to the object, not when the object is created.