On this page:

6.12 Surrogates

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.


(surrogate use-wrapper-proc method-spec ...)

use-wrapper-proc = #:use-wrapper-proc
method-spec = (augment 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.

If #:use-wrapper-proc does not appear, the host mixin adds one field surrogate. to its argument. It also adds getter and setter methods, get-surrogate, set-surrogate, get-surrogate-wrapper-proc, and set-surrogate-wrapper-proc for changing the fields. 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.

If #:use-wrapper-proc does appear, the the host mixin adds both the surrogate field (with its getters and setters) and a a second field, surrogate-wrapper-proc and its getter and setter methods, get-surrogate-wrapper-proc and set-surrogate-wrapper-proc. The surrogate-wrapper-proc field holds a procedure whose contract is (-> (-> any) (-> any) any). The function is invoked with two thunks. The first one is a fallback that invokes the original object’s method, skipping the surrogate. The other one invokes the surrogate.
(λ (fallback-thunk surrogate-thunk)
which means that it simply defers to the method being invoked on the surrogate. The surrogate-wrapper-proc capability is part of the surrogate so that the dynamic extent of the calls to the surrogate can be adjusted (by, for example, changing the values of parameters). The surrogate-wrapper-proc is also invoked when calling the on-disable-surrogate and on-enable-surrogate methods of the surrogate.

The host mixin has a single overriding method for each method-id in the surrogate form (even the ones specified with augment. 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
            (λ (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.