On this page:
surrogate

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.

syntax

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

 
use-wrapper-proc = #:use-wrapper-proc
  | 
     
method-spec = (augment default-expr method-id arg-spec ...)
  | (override method-id arg-spec ...)
     
arg-spec = (id ...)
  | id
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 a single private field to its argument. It also adds getter and setter methods get-surrogate and set-surrogate to get and set the value of 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.

If #:use-wrapper-proc does appear, the host mixin adds a second private field and its getter and setter methods get-surrogate-wrapper-proc and set-surrogate-wrapper-proc. The additional field holds a wrapper procedure whose contract is (-> (-> any) (-> any) any), so the procedure is invoked with two thunks. The first thunk is a fallback that invokes the original object’s method, skipping the surrogate. The second thunk invokes the surrogate. The default wrapper procedure is
(λ (fallback-thunk surrogate-thunk)
  (surrogate-thunk))
That is, it simply defers to the method being invoked on the surrogate. Note that wrapper procedure can adjust the dynamic extent of calls to the surrogate by, for example, changing the values of parameters. The wrapper procedure 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 private surrogate field. If the field value is #f, the method just returns the result of invoking the super or inner method. If the field value 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))

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.