{"size": 5633, "ext": "scrbl", "max_stars_repo_path": "pkgs/racket-doc/syntax/scribblings/parse/ex-many-kws.scrbl", "max_stars_repo_name": "lkh01/racket", "max_stars_repo_stars_event_min_datetime": "2018-07-29T04:13:52.000Z", "max_stars_repo_stars_event_max_datetime": "2022-02-14T16:11:17.000Z", "max_issues_repo_path": "pkgs/racket-doc/syntax/scribblings/parse/ex-many-kws.scrbl", "max_issues_repo_name": "lkh01/racket", "max_issues_repo_issues_event_min_datetime": "2017-08-25T20:26:37.000Z", "max_issues_repo_issues_event_max_datetime": "2022-02-26T17:48:40.000Z", "max_forks_repo_path": "pkgs/racket-doc/syntax/scribblings/parse/ex-many-kws.scrbl", "max_forks_repo_name": "lkh01/racket", "max_forks_repo_forks_event_min_datetime": "2016-04-25T11:47:57.000Z", "max_forks_repo_forks_event_max_datetime": "2017-05-09T14:52:48.000Z"}
{"max_stars_count": 10, "max_issues_count": 15, "max_forks_count": 3, "avg_line_length": 39.6690140845, "max_line_length": 72, "alphanum_fraction": 0.6635895615}
#lang scribble/doc
@(require scribble/manual
          scribble/struct
          scribble/decode
          scribble/eval
          "parse-common.rkt"
          (for-label racket/class))

@(define the-eval (make-sp-eval))

@title{More Keyword Arguments}

This section shows how to express the syntax of @racket[struct]'s
optional keyword arguments using @racket[syntax-parse] patterns.

The part of @racket[struct]'s syntax that is difficult to specify is
the sequence of struct options. Let's get the easy part out of the way
first.

@interaction[#:eval the-eval
(define-splicing-syntax-class maybe-super
  (pattern (~seq super:id))
  (pattern (~seq)))

(define-syntax-class field-option
  (pattern #:mutable)
  (pattern #:auto))

(define-syntax-class field
  (pattern field:id
           #:with (option ...) '())
  (pattern [field:id option:field-option ...]))
]

Given those auxiliary syntax classes, here is a first approximation of
the main pattern, including the struct options:
@racketblock[
(struct name:id super:maybe-super (field:field ...)
  (~alt (~seq #:mutable)
        (~seq #:super super-expr:expr)
        (~seq #:inspector inspector:expr)
        (~seq #:auto-value auto:expr)
        (~seq #:guard guard:expr)
        (~seq #:property prop:expr prop-val:expr)
        (~seq #:transparent)
        (~seq #:prefab)
        (~seq #:constructor-name constructor-name:id)
        (~seq #:extra-constructor-name extra-constructor-name:id)
        (~seq #:omit-define-syntaxes)
        (~seq #:omit-define-values))
  ...)
]
The fact that @racket[expr] does not match keywords helps in the case
where the programmer omits a keyword's argument; instead of accepting
the next keyword as the argument expression, @racket[syntax-parse]
reports that an expression was expected.

There are two main problems with the pattern above:
@itemize[
@item{There's no way to tell whether a zero-argument keyword like
@racket[#:mutable] was seen.}
@item{Some options, like @racket[#:mutable], should appear at most
once.}
]

The first problem can be remedied using @racket[~and] patterns to bind
a pattern variable to the keyword itself, as in this sub-pattern:
@racketblock[
(~seq (~and #:mutable mutable-kw))
]
The second problem can be solved using @emph{repetition constraints}:
@racketblock[
(struct name:id super:maybe-super (field:field ...)
  (~alt (~optional (~seq (~and #:mutable mutable-kw)))
        (~optional (~seq #:super super-expr:expr))
        (~optional (~seq #:inspector inspector:expr))
        (~optional (~seq #:auto-value auto:expr))
        (~optional (~seq #:guard guard:expr))
        (~seq #:property prop:expr prop-val:expr)
        (~optional (~seq (~and #:transparent transparent-kw)))
        (~optional (~seq (~and #:prefab prefab-kw)))
        (~optional (~seq #:constructor-name constructor-name:id))
        (~optional
         (~seq #:extra-constructor-name extra-constructor-name:id))
        (~optional
         (~seq (~and #:omit-define-syntaxes omit-def-stxs-kw)))
        (~optional (~seq (~and #:omit-define-values omit-def-vals-kw))))
  ...)
]
The @racket[~optional] repetition constraint indicates that an
alternative can appear at most once. (There is a @racket[~once] form
that means it must appear exactly once.) In @racket[struct]'s keyword
options, only @racket[#:property] may occur any number of times.

There are still some problems, though. Without additional help,
@racket[~optional] does not report particularly good errors. We must
give it the language to use, just as we had to give descriptions to
sub-patterns via syntax classes. Also, some related options are
mutually exclusive, such as @racket[#:inspector],
@racket[#:transparent], and @racket[#:prefab].

@racketblock[
(struct name:id super:maybe-super (field:field ...)
  (~alt (~optional
         (~or* (~seq #:inspector inspector:expr)
               (~seq (~and #:transparent transparent-kw))
               (~seq (~and #:prefab prefab-kw)))
         #:name "#:inspector, #:transparent, or #:prefab option")
        (~optional (~seq (~and #:mutable mutable-kw))
                   #:name "#:mutable option")
        (~optional (~seq #:super super-expr:expr)
                   #:name "#:super option")
        (~optional (~seq #:auto-value auto:expr)
                   #:name "#:auto-value option")
        (~optional (~seq #:guard guard:expr)
                   #:name "#:guard option")
        (~seq #:property prop:expr prop-val:expr)
        (~optional (~seq #:constructor-name constructor-name:id)
                   #:name "#:constructor-name option")
        (~optional
          (~seq #:extra-constructor-name extra-constructor-name:id)
          #:name "#:extra-constructor-name option")
        (~optional (~seq (~and #:omit-define-syntaxes omit-def-stxs-kw))
                   #:name "#:omit-define-syntaxes option")
        (~optional (~seq (~and #:omit-define-values omit-def-vals-kw))
                   #:name "#:omit-define-values option"))
  ...)
]
Here we have grouped the three incompatible options together under a
single @racket[~optional] constraint. That means that at most one of
any of those options is allowed. We have given names to the optional
clauses. See @racket[~optional] for other customization options.

Note that there are other constraints that we have not represented in
the pattern. For example, @racket[#:prefab] is also incompatible with
both @racket[#:guard] and @racket[#:property]. Repetition constraints
cannot express arbitrary incompatibility relations. The best way to
handle such constraints is with a side condition using
@racket[#:fail-when].

@(close-eval the-eval)
