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 | Unsafe for Clause Transforms
9 | Source Locations
10 | Preserving Source Locations
11 | Non-Module Compilation And Expansion
12 | Trusting Standard Recertifying Transformers
13 | Attaching Documentation to Exports
14 | Contracts for Macro Subexpressions
15 | Macro Testing
16 | Internal-Definition Context Helpers
| 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 | Unwindable State
1.10 | Configuring Error Reporting
1.11 | Debugging and Inspection Tools
1.12 | Experimental
1.13 | Minimal Library

▼ | 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

► | 1.2.2 | Optional Keyword Arguments

1.2.2.1 | Optional Arguments with ~?
1.2.2.2 | Optional Arguments with define-splicing-syntax-class

On this page:

1.2.2.1 Optional Arguments with ~?
1.2.2.2 Optional Arguments with define-splicing-syntax-class

Racket
top contents ← 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 simply 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 first check the attribute using (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))

##### 1.2.2.1 Optional Arguments with ~?🔗ℹ

The ~? template form provides a compact alternative to explicitly testing attribute values. Here’s one way to do it:

> > (define-syntax (mycond stx)
> (syntax-parse stx
> [(mycond (~optional (~seq #:error-on-fallthrough who:expr))
> clause ...)
> #'(mycond* (~? (~@ #t who) (~@ #f #f)) clause ...)]))

If who matched, then the ~? subtemplate splices in the two terms #t who into the enclosing template (~@ is the template splicing form). Otherwise, it splices in #f #f.

Here’s an alternative definition that re-uses Racket’s cond macro:

> > (define-syntax (mycond stx)
> (syntax-parse stx
> [(mycond (~optional (~seq #:error-on-fallthrough who:expr))
> clause ...)
> #'(cond clause ... (~? [else (error who "no clause matched")] (~@)))]))

In this version, we optionally insert an else clause at the end to signal the error; otherwise we use cond’s fall-through behavior (that is, returning (void)).

If the second subtemplate of a ~? template is (~@)—that is, it produces no terms at all—the second subtemplate can be omitted.

##### 1.2.2.2 Optional Arguments with define-splicing-syntax-class🔗ℹ

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-fallthrough 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, but it is less convenient. Splicing syntax classes also closely parallel the style of grammars in macro documentation.

top contents ← prev up next →