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


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