Skip to content

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

Open
@sorawee

Description

@sorawee

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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions