Next: loops generators let, Previous: loops generators port, Up: loops generators [Index]
First the arg expressions are evaluated into a[1], a[2], …, a[n] and then a global dispatch procedure is used to dispatch on the number and types of the arguments and run the resulting generator. Initially the following cases are recognized, with i \in {1, …, n}:
If for all i:
(list? a[i]) ⇒ #t
If for all i:
(string? a[i]) ⇒ #t
If for all i:
(vector? a[i]) ⇒ #t
If n \in {1, …, 3} and for all i \in {1, …, n}:
(and (integer? a[i]) (exact? a[i])) ⇒ #t
If n \in {1, …, 3} and for all i \in {1, …, n}:
(real? a[i]) ⇒ #t
If n = 2 and for all i \in {1, 2}:
(char? a[i]) ⇒ #t
If n \in {1, 2} and:
(and (input-port? a[1]) (procedure? a[2])) ⇒ #t
The current dispatcher can be retrieved as (:-dispatch-ref)
, a
new dispatcher ‘d’ can be installed by (:-dispatch-set! d)
yielding an unspecified result, and a copy of the initial dispatcher can
be obtained as (make-initial-:-dispatch)
. Please refer to the
section below for recommendation how to add cases to the dispatcher.
Runs the variables through a sequence defined by dispatch and the arg expressions. The purpose of ‘:dispatched’ is implementing dispatched generators, in particular the predefined dispatching generator ‘:’.
The working of ‘:dispatched’ is as follows: First dispatch and the arg expressions are evaluated, resulting in a procedure d (the “dispatcher”) and the values a[1], a[2], …, a[n]. Then:
(d (list a[1] a[2] ... a[n]))
is evaluated, resulting in a value g. If g is not a procedure then the dispatcher did not recognize the argument list and an error is raised. Otherwise the “generator procedure” g is used to run vars through a sequence of values.
The sequence defined by g is obtained by repeated evaluation of
(g empty)
until the result is empty. In other
words, g indicates the end of the sequence by returning its only
argument, for which the caller has provided an object distinct from
anything g can produce.
The definition of dispatchers is greatly simplified by the macro
:generator-proc
that constructs a generator procedure from a
typed generator. Let (g var arg0 arg
...)
be an instance of the ?generator syntax, for example an
application–specific typed generator, with a single variable var
and no index variable. Then:
(:generator-proc (g arg0 arg ...)) ⇒ g
where the generator procedure g runs through the list:
(list-ec (g var arg0 arg ...) var)
In order to define a new dispatching generator (say :my
) first a
dispatching procedure (say :my-dispatch
) is defined. The
dispatcher will be called with a single (!) argument containing the list
of all values to dispatch on. To enable informative error messages, the
dispatcher should return a descriptive object (e.g. a symbol for the
module name) when it is called with the empty list. Otherwise (if there
is at least one value to dispatch on), the dispatcher must either return
a generator procedure or #f
(which means: no interest). As an
example, the following skeleton code defines a dispatcher similar to the
initial dispatcher of :
:
(define (:my-dispatch args) (case (length args) ((1) (let ((a1 (car args))) (cond ((list? a1) (:generator-proc (:list a1))) ((string? a1) (:generator-proc (:string a1))) ...more unary cases... (else #f)))) ((2) (let ((a1 (car args)) (a2 (cadr args))) (cond ((and (list? a1) (list? a2)) (:generator-proc (:list a1 a2))) ...more binary cases... (else #f)))) ...more arity cases... (else (cond ((every?-ec (:list a args) (list? a)) (:generator-proc (:list (apply append args)))) ...more large variable arity cases... (else #f)))))
Once the dispatcher has been defined, the following macro implements the new dispatching generator:
(define-syntax :my (syntax-rules (index) ((:my cc var (index i) arg0 arg ...) (:dispatched cc var (index i) :my-dispatch arg0 arg ...)) ((:my cc var arg0 arg ...) (:dispatched cc var :my-dispatch arg0 arg ...))))
This method of extension yields complete control of the dispatching process. Other modules can only add cases to ‘:my’ if they have access to ‘:my-dispatch’.
An alternative to adding a new dispatched generator is to extend the predefined generator ‘:’. Technically, extending ‘:’ means installing a new global dispatching procedure using ‘:-dispatch-set!’ as described above. In most cases, however, the already installed dispatcher should be extended by new cases. The following procedure is a utility for doing so:
(dispatch-union d1 d2) ⇒ d
where the new dispatcher d recognizes the union of the cases
recognized by the dispatchers d1 and d2. The new dispatcher
always tries both component dispatchers and raises an error in case of
conflict. The identification returned by (d)
is the
concatenation of the component identifications (d1)
and
(d2)
, enclosed in lists if necessary. For illustration, consider
the following code:
(define (example-dispatch args) (cond ((null? args) 'example) ((and (= (length args) 1) (symbol? (car args)) ) (:generator-proc (:string (symbol->string (car args))))) (else #f))) (:-dispatch-set! (dispatch-union (:-dispatch-ref) example-dispatch))
After evaluation of this code, the following example will work:
(list-ec (: c 'abc) c) ⇒ (#\a #\b #\c)
Adding cases to ‘:’ is particularly useful for frequent cases of interactive input. Be warned, however, that the advantage of global extension also carries the danger of conflicts, unexpected side–effects, and slow dispatching.
Next: loops generators let, Previous: loops generators port, Up: loops generators [Index]