Next: makers api, Up: makers [Index]
It happens to define a function or macro accepting a number of arguments greater than 3:
(define (the-func a b c d e f g h i) ...)
in these cases it can be difficult to remember the order of the
arguments; it can also be desirable to define default values for some or
all of the arguments, so that at the call site some arguments can be
omitted in the source code. The syntax define-maker
implements a
solution to this problem.
The following examples show the expansion of a simple maker with neither fixed nor variable arguments, only optional clauses arguments:
(import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntaxes alpha: beta: gamma:) (define-maker doit list ((alpha: 1) (beta: 2) (gamma: 3))) (doit) → (list 1 2 3) (doit (alpha: 10)) → (list 10 2 3) (doit (beta: 20)) → (list 1 20 3) (doit (gamma: 30)) → (list 1 2 30) (doit (alpha: 10) (beta: 20)) → (list 10 20 3) (doit (alpha: 10) (gamma: 30)) → (list 10 2 30) (doit (gamma: 30) (beta: 20)) → (list 1 20 30) (doit (alpha: 10) (beta: 20) (gamma: 30)) → (list 10 20 30) (let ((b 7)) (doit (beta: (+ 6 (* 2 b))) (alpha: (+ 2 8))) → (list (+ 2 8) (+ 6 (* 2 b)) 3) #f) (doit (alpha: 10 20 30)) → (list 1 (list 10 20 30) 3)
notice the last example: when multiple values are used in an argument’s
clause, they are enclosed in a list
form; the following examples
show the expansion of a maker with both fixed and variable arguments,
plus optional clauses arguments:
(import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntaxes alpha: beta: gamma:) (define-maker (doit a b) (list #\a #\b) ((alpha: 1) (beta: 2) (gamma: 3))) (doit #\p #\q) → (list #\a #\b #\p #\q 1 2 3) (doit #\p #\q (alpha: 10)) → (list #\a #\b #\p #\q 10 2 3) (doit #\p #\q (beta: 20)) → (list #\a #\b #\p #\q 1 20 3) (doit #\p #\q (gamma: 30)) → (list #\a #\b #\p #\q 1 2 30) (doit #\p #\q (alpha: 10) (beta: 20)) → (list #\a #\b #\p #\q 10 20 3) (doit #\p #\q (alpha: 10) (gamma: 30)) → (list #\a #\b #\p #\q 10 2 30) (doit #\p #\q (gamma: 30) (beta: 20)) → (list #\a #\b #\p #\q 1 20 30)
Each default value can be any expression and it is evaluated every time the maker is used:
(import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntaxes alpha: beta: gamma:) (define g (let ((counter 0)) (lambda () (set! counter (+ 1 counter)) counter))) (define default2 (- (/ 9 3) 1)) (define-maker doit list ((alpha: 1) (beta: default2) (gamma: (g)))) (doit) ⇒ ( 1 2 1) (doit (alpha: 10)) ⇒ (10 2 2) (doit (beta: 20)) ⇒ ( 1 20 3) (doit (gamma: 30)) ⇒ ( 1 2 30)
A maker invocation can expand itself into another macro use; this allows us to detect whether an optional argument was used or not:
(import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntaxes alpha: beta: gamma:) (define-maker doit subdoit ((alpha: 1) (beta: 2) (gamma: sentinel))) (define-syntax subdoit (lambda (stx) (syntax-case stx () ((_ ?alpha ?beta ?gamma) (and (identifier? #'?gamma) (free-identifier=? #'?gamma #'sentinel)) #'(list ?alpha ?beta 3)) ((_ ?alpha ?beta ?gamma) #'(list ?alpha ?beta ?gamma)) ))) (doit) ⇒ ( 1 2 3) (doit (alpha: 10)) ⇒ (10 2 3) (doit (beta: 20)) ⇒ ( 1 20 3) (doit (gamma: 30)) ⇒ ( 1 2 30)
and also to “unpack” multiple values used in the same argument clause:
(import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntaxes alpha: beta: gamma:) (define-maker doit subdoit ((alpha: 1) (beta: 2) (gamma: 3))) (define-syntax subdoit (lambda (stx) (syntax-case stx (list) ((_ ?alpha (list ?beta0 ...) ?gamma) #'(list ?alpha ?beta0 ... ?gamma)) ((_ ?alpha ?beta ?gamma) #'(list ?alpha ?beta ?gamma)) ))) (doit (alpha: 10) (beta: #\a #\b #\c) (gamma: 30)) ⇒ (10 #\a #\b #\c 30)
We can define a maker in the body of a library and export it; we just have to remember to export the auxiliary syntaxes, too:
(library (the-lib) (export doit alpha beta gamma) (import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntaxes alpha beta gamma) (define-maker doit list ((alpha 1) (beta 2) (gamma 3)))) (library (exec) (export) (import (rnrs) (prefix (the-lib) lib.)) (lib.doit) → (list 1 2 3) (lib.doit (lib.alpha 10)) → (list 10 2 3) (lib.doit (lib.beta 20)) → (list 1 20 3) (lib.doit (lib.gamma 30)) → (list 1 2 30) )
A number of options can be specified to customise the parsing of clauses. We can specify if a clause is mandatory or optional:
(import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntaxes alpha: beta: gamma:) (define-maker doit list ((alpha: 1 (mk.mandatory)) (beta: 2 (mk.optional)) (gamma: 3))) (doit (alpha: 10)) → (list 10 2 3) (doit (beta: 20)) error→ missing clause "alpha:"
the default is for all the clauses to be optional. We can specify that a clause must be used along one or more other clauses:
(import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntaxes alpha: beta: gamma:) (define-maker doit list ((alpha: 1 (mk.with beta: gamma:)) (beta: 2) (gamma: 3))) (doit (alpha: 10) (beta: 20) (gamma: 30)) → (list 10 20 30) (doit (beta: 20)) → (list 1 20 3) (doit (alpha: 10) (gamma: 30)) error→ missing clause "beta:"
or we can specify that a clause must not be used along one or more other clauses:
(import (vicare) (prefix (vicare language-extensions makers) mk.)) (define-auxiliary-syntax alpha: beta: gamma:) (define-maker doit list ((alpha: 1 (mk.without beta: gamma:)) (beta: 2) (gamma: 3))) (doit (alpha: 10) (beta: 20) (gamma: 30)) error→ invalid clauses mix (doit (beta: 20)) → (list 1 20 3)
Next: makers api, Up: makers [Index]