Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flaggable #%app: #%app that understands flags #14

Open
sorawee opened this issue Aug 6, 2021 · 0 comments
Open

Flaggable #%app: #%app that understands flags #14

sorawee opened this issue Aug 6, 2021 · 0 comments

Comments

@sorawee
Copy link

sorawee commented Aug 6, 2021

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

@sorawee sorawee changed the title Flaggable #%app: #%app that understand flags Flaggable #%app: #%app that understands flags Aug 6, 2021
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 10, 2021
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 27, 2021
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant