8.2.2 Optional keyword arguments
This section explains how to write a macro that accepts (simple)
optional keyword arguments. We use the example mycond, which
is like Racket’s cond except that it takes an optional
keyword argument that controls what happens if none of the clauses
match.
Optional keyword arguments are supported via head
patterns. Unlike normal patterns, which match one term, head patterns
can match a variable number of subterms in a list. Some important
head-pattern forms are ~seq, ~or, and
~optional.
Here’s one way to do it:
|
> (define-syntax mycond* | (syntax-rules () | [(mycond error? who [question answer] . clauses) | (if question answer (mycond* error? who . clauses))] | [(mycond #t who) | (error who "no clauses matched")] | [(mycond #f _) | (void)])) |
|
We cannot write #'who in the macro’s right-hand side, because
the who attribute does not receive a value if the keyword
argument is omitted. Instead we must write (attribute who),
which produces #f if matching did not assign a value to the
attribute.
> (mycond [(even? 13) 'blue] | [(odd? 4) 'red]) |
|
> (mycond #:error-on-fallthrough 'myfun | [(even? 13) 'blue] | [(odd? 4) 'red]) |
|
myfun: no clauses matched |
There’s a simpler way of writing the
~or pattern above:
Yet another way is to introduce a
splicing syntax class, which
is like an ordinary syntax class but for head patterns.
Defining a splicing syntax class also makes it easy to eliminate the
case analysis we did before using attribute by defining
error? and who as attributes within both of the
syntax class’s variants. (This is possible to do in the inline pattern
version too, using ~and and ~parse, just less
convenient.) Splicing syntax classes also closely parallel the style
of grammars in macro documentation.