On this page:
surrogate

6.12 Surrogates

 (require racket/surrogate) package: base
The bindings documented in this section are provided by the racket/surrogate library, not racket/base or racket.

The racket/surrogate library provides an abstraction for building an instance of the proxy design pattern. The pattern consists of two objects, a host and a surrogate object. The host object delegates method calls to its surrogate object. Each host has a dynamically assigned surrogate, so an object can completely change its behavior merely by changing the surrogate.

syntax

(surrogate method-spec ...)

 
method-spec = (method-id arg-spec ...)
  | (override method-id arg-spec ...)
  | 
(override-final method-id (lambda () default-expr)
                arg-spec ...)
     
arg-spec = (id ...)
  | id
If neither override nor override-final is specified for a method-id, then override is assumed.

The surrogate form produces four values: a host mixin (a procedure that accepts and returns a class), a host interface, a surrogate class, and a surrogate interface.

The host mixin adds one additional field, surrogate, to its argument. It also adds a getter method, get-surrogate, and a setter method, set-surrogate, for changing the field. The set-surrogate method accepts instances of the class returned by the surrogate form or #f, and it updates the field with its argument; then, set-surrogate calls the on-disable-surrogate on the previous value of the field and on-enable-surrogate for the new value of the field. The get-surrogate method returns the current value of the field.

The host mixin has a single overriding method for each method-id in the surrogate form. Each of these methods is defined with a case-lambda with one arm for each arg-spec. Each arm has the variables as arguments in the arg-spec. The body of each method tests the surrogate field. If it is #f, the method just returns the result of invoking the super or inner method. If the surrogate field is not #f, the corresponding method of the object in the field is invoked. This method receives the same arguments as the original method, plus two extras. The extra arguments come at the beginning of the argument list. The first is the original object. The second is a procedure that calls the super or inner method (i.e., the method of the class that is passed to the mixin or an extension, or the method in an overriding class), with the arguments that the procedure receives.

For example, the host-mixin for this surrogate:

(surrogate (override m (x y z)))

will override the m method and call the surrogate like this:
(define/override (m x y z)
  (if surrogate
      (send surrogate m
            this
            (λ (x y z) (super m x y z))
            x y z)
      (super m x y z)))
where surrogate is bound to the value most recently passed to the host mixin’s set-surrogate method.

The host interface has the names set-surrogate, get-surrogate, and each of the method-ids in the original form.

The surrogate class has a single public method for each method-id in the surrogate form. These methods are invoked by classes constructed by the mixin. Each has a corresponding method signature, as described in the above paragraph. Each method just passes its argument along to the super procedure it receives.

In the example above, this is the m method in the surrogate class:
(define/public (m original-object original-super x y z)
  (original-super x y z))

Note: if you derive a class from the surrogate class, do not both call the super argument and the super method of the surrogate class itself. Only call one or the other, since the default methods call the super argument.

Finally, the interface contains all of the names specified in surrogate’s argument, plus on-enable-surrogate and on-disable-surrogate. The class returned by surrogate implements this interface.