Open
Description
Many functions accept a lot of optional "boolean" keyword arguments. These arguments are known as flags. As an example:
(define (string-trim s #:left? [left? #f] #:right? [#:right? #f])
(list s left? right?))
has two flags: #:left?
and #:right?
.
The function then can be called with:
(string-trim " 1 2 3 " #:left? #t #:right? #t)
Flaggable #%app
allows users to instead write:
(string-trim " 1 2 3 " #:left? #:right?)
That is, a keyword argument that doesn't have a "value" will default the value to #t
. Explicit "value" is still supported.
This does come at a cost: all keyword arguments must be specified after positional arguments to avoid ambiguity. Without this restriction, it's hard to tell whether:
(f #:a 1)
is meant to be itself or:
(f 1 #:a #t)
Macro
#lang racket
(require syntax/parse/define
(only-in racket [#%app racket:#%app]))
(begin-for-syntax
(define-splicing-syntax-class arg/keyword
#:attributes (k v)
;; first case: something like #:a 1
(pattern {~seq k:keyword v:expr})
;; second case: something like #:a.
(pattern {~seq k:keyword}
#:with v #'#t)))
(define-syntax-parse-rule (#%app f arg/no-keyword:expr ... arg/keyword:arg/keyword ...)
(racket:#%app f arg/no-keyword ... {~@ arg/keyword.k arg/keyword.v} ...))
Note: inspired by https://www.reddit.com/r/Racket/comments/oytknk/keyword_arguments_without_values/h7w67dd/, though I had thought of doing it before, too.
Example usage:
(define (f c #:a [a #f] #:b [b #f])
(list c a b))
(f 0 #:a #:b) ;=> '(0 #t #t)
(f 0 #:a) ;=> '(0 #t #f)
(f 0 #:b) ;=> '(0 #f #t)
(f 0 #:a 10 #:b) ;=> '(0 10 #t)
(f 0 #:a #:b 20) ;=> '(0 #t 20)
(f 0 #:a 10 #:b 20) ;=> '(0 10 20)
(f 0) ;=> '(0 #f #f)
#;(f #:a 0 1)
; #%app: expected keyword
; parsing context:
; while parsing arg/keyword in: 1
Before code
(f 0 #:a #t #:b #t) ;=> '(0 #t #t)
(f 0 #:a #t) ;=> '(0 #t #f)
(f 0 #:b #t) ;=> '(0 #f #t)
(f 0 #:a 10 #:b #t) ;=> '(0 10 #t)
(f 0 #:a #t #:b 20) ;=> '(0 #t 20)
(f 0 #:a 10 #:b 20) ;=> '(0 10 20)
(f 0) ;=> '(0 #f #f)
Licence
MIT / CC-BY 4.0
Metadata
Metadata
Assignees
Labels
No labels