1.2.2 Optional Keyword Arguments

► |  | Syntax: Meta-Programming Helpers

1 | Parsing and Specifying Syntax
2 | Syntax Object Helpers
3 | Datum Pattern Matching
4 | Module-Processing Helpers
5 | Macro Transformer Helpers
6 | Reader Helpers
7 | Parsing for Bodies
8 | Source Locations
9 | Preserving Source Locations
10 | Non-Module Compilation And Expansion
11 | Trusting Standard Recertifying Transformers
12 | Attaching Documentation to Exports
| Index

► | 1 | Parsing and Specifying Syntax

1.1 | Introduction
1.2 | Examples
1.3 | Parsing Syntax
1.4 | Specifying Syntax with Syntax Classes
1.5 | Syntax Patterns
1.6 | Defining Simple Macros
1.7 | Literal Sets and Conventions
1.8 | Library Syntax Classes and Literal Sets
1.9 | Debugging and Inspection Tools
1.10 | Experimental

▼ | 1.2 | Examples

1.2.1 | Phases and Reusable Syntax Classes
1.2.2 | Optional Keyword Arguments
1.2.3 | Variants with Uniform Meanings
1.2.4 | Variants with Varied Meanings
1.2.5 | More Keyword Arguments
1.2.6 | Contracts on Macro Sub-expressions

top ← prev up next →

##### 1.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 stx)
> (syntax-parse stx
> [(mycond (~or (~seq #:error-on-fallthrough who:expr) (~seq))
> clause ...)
> (with-syntax ([error? (if (attribute who) #'#t #'#f)]
> [who (or (attribute who) #'#f)])
> #'(mycond* error? who clause ...))]))
> > (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:

> (~optional (~seq #:error-on-fallthrough who:expr))

Yet another way is to introduce a splicing syntax class, which is like an ordinary syntax class but for head patterns.

> > (define-syntax (mycond stx)
> (define-splicing-syntax-class maybe-fallthrough-option
> (pattern (~seq #:error-on-fallthough who:expr)
> #:with error? #'#t)
> (pattern (~seq)
> #:with error? #'#f
> #:with who #'#f))
> (syntax-parse stx
> [(mycond fo:maybe-fallthrough-option clause ...)
> #'(mycond* fo.error? fo.who clause ...)]))

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.

top ← prev up next →