Racket Generic Binding Forms
(require generic-bind) | package: generic-bind |
This library implements new Racket binding forms (ie, ~define, ~lambda, ~let, etc.) that support generic binding instances. A generic binding instance is a syntax object that implements an "interface" (currently just some syntax properties) that the binding forms then use to determine how to proceed with the binding.
This moves the binding "logic" to the binding site itself and enables one binding form to support many different binding modes (ie, match or values), rather than have to manually implement a version of each binding form for all the different combinations (ie, define-values, match-define, match-define-values, etc.).
The hope is that the forms in this library can be used in place of their analogous Racket counterparts.
1 Generic Binding Instances
The forms described in this section may be used only in binding positions of binding forms that support generic bindings. Any other use is invalid. For examples, see the next section, Core Generic Binding Forms.
bindings that may be used anywhere, and
bindings that may only be used in "define" contexts.
The second category is a subset of the first and is needed in order to handle Racket’s multiple return values. Since Racket functions cannot receive values, we must know what we are binding at the time of the binding. For example, this means that ~let may support values binding, but ~define or ~lambda may not. Thus, the second category essentially the first, but without values-bindings.
A few generic binding instances are currently supported. Defining new generic bindings is currently limited to new match-specific binding instances. See Implementing New Generic Binding Instances for more information.
syntax
($ match-pattern)
Other generic binding instances can be nested within the match pattern.
syntax
(~vs b ...)
b = define-allowable-generic-binding
Currently supports one-level of nested generic binding instances. In other words, each binding site in the ~vs form may be either an identifier, or another generic binding instance. However, any nested binding positions are considered "define" contexts. This means that one cannot nest ~vs bindings.
syntax
(⋈ b ...)
syntax
($: x xs)
syntax
($stx pattern pattern-directive ...)
syntax
($and b ...)
b = generic-binding
syntax
($c db contract-expr)
($c b (values contract-expr ...))
db = define-allowable-generic-binding b = generic-binding
2 Core Generic Binding Forms
syntax
(~define b body)
(~define (f db ...) body ...)
b = generic-binding | identifier? db = define-allowable-generic-binding | identifier?
When using ~define to define a function, any generic binding must be define-allowable. (So the ~vs values-binding form is not allowed.)
> (~define ($ (list y1 y2)) (list 10 20))
> y1 10
> y2 20
> (~define ($ (list (cons y3 y4) y5)) (list (cons 20 30) 40))
> y3 20
> y4 30
> y5 40
> (~define (⋈ v1 v2) (values 101 202))
> v1 101
> v2 202
> (~define (⋈ ($ (list x4 x5)) x6) (values (list 99 999) 9999))
> x4 99
> x5 999
> x6 9999
> (struct A (x y))
> (~define ($ (A x7 y7)) (A 101 202))
> x7 101
> y7 202
> (~define ($stx ((~seq kw:keyword arg:expr) ...)) #'(#:a 1 #:b 2 #:c 3))
> #'(kw ...) #<syntax:25:0 (#:a #:b #:c)>
> #'(arg ...) #<syntax:26:0 (1 2 3)>
> #'([kw . arg] ...) #<syntax:27:0 ((#:a . 1) (#:b . 2) (#:c . 3))>
> (~define (f1 x [y 10] #:z [z 0]) (+ x y z))
> (f1 100) 110
> (f1 100 200) 300
> (f1 100 200 #:z 300) 600
> (~define (f3 . rst) rst)
> (f3 1 2 3) '(1 2 3)
> (f3) '()
> (~define (f4 x y . rst) (cons x (cons y rst)))
> (f4 1 2 3 4 5 6) '(1 2 3 4 5 6)
> (f4 1 2) '(1 2)
> (~define (f2 ($ (list x y))) (- x y))
> (f2 (list 145 45)) 100
> (~define (g1 ($ (list (list a b) y ...))) (apply + a b y))
> (g1 (list (list 101 202) 303)) 606
> (~define (f5 ($ (list (list x y) z)) . rst) (cons x (cons y (cons z rst))))
> (f5 (list (list 1 2) 3)) '(1 2 3)
> (f5 (list (list 1 2) 3) 4 5) '(1 2 3 4 5)
> (~define (gg ($ xxx)) (add1 xxx))
> (gg 10001) 10002
> (~define (ggg ($ _)) 12345)
> (ggg 5432) 12345
> (~define (gggg ($ 11111)) 22222)
> (gggg 111) match-define: no matching clause for 111
> (gggg 11111) 22222
> (~define (f20 ($list x y z : xs)) (+ x y z (length xs)))
> (f20 (list 10 20 30 40 50 60 70 80 90)) 66
> (~define (f21 ($: x xs)) (+ x (length xs)))
> (f21 (list 10 20 30 40 50 60 70 80 90)) 18
> (~define (fkw1 [($list x y) (list 1 2)]) (+ x y 10))
> (fkw1) 13
> (fkw1 (list 10 20)) 40
> (fkw1 10) match-define: no matching clause for 10
> (~define (fkw2 #:A ($list x y)) (+ x y 10))
> (fkw2 #:A (list 1 2)) 13
> (~define (fkw3 #:B [($list x y) (list 1 2)]) (+ x y 10))
> (fkw3 #:B (list 10 20)) 40
> (fkw3 #:B 10) match-define: no matching clause for 10
syntax
(~def ...)
syntax
(~d ...)
syntax
(~define/contract b contract-expr body)
(~define/contract (f db ...) contract-expr body ...)
b = generic-binding | identifier? db = define-allowable-generic-binding | identifier?
syntax
(~lambda db body ...)
(~lambda (db ...) body ...)
db = define-allowable-generic-binding | identifier?
If a single identifier is given with no parentheses, then the arguments are put in a list, just like lambda. A single generic bind instance may also be used without parens. If a list of bindings if given, then each may be either an identifier or a generic bind instance.
syntax
(~lam ...)
syntax
(~l ...)
syntax
(~λ ...)
> ((~λ (x) x) 111) 111
> ((~λ rst rst) 1 2 3) '(1 2 3)
> ((~λ (x [y 0] #:z z #:a [a 10]) (+ x y z a)) 1 #:z 10) 21
> ((~λ (x [y 0] #:z z #:a [a 10]) (+ x y z a)) 1 22 #:z 10) 43
> ((~λ (x [y 0] #:z z #:a [a 10]) (+ x y z a)) 1 #:z 10 #:a 111) 122
> ((~lambda (x [y 0] #:z z #:a [a 10]) (+ x y z a)) 1 #:z 10 #:a 111) 122
> ((~lambda ($: x xs) (append xs (list x))) (list 1 2 3 4 5)) '(2 3 4 5 1)
> ((~lambda (f ($: x xs)) (cons (f x) xs)) add1 (list 1 2 3 4)) '(2 2 3 4)
> ((~lambda (f ($list)) (f 1)) add1 null) 2
> ((~lambda (f ($list)) (f 1)) add1 (list 1 2 3)) match-define: no matching clause for '(1 2 3)
> ((~λ (($ (list x y)) ($ (cons a b))) (+ a b x y)) (list 1 2) (cons 3 4)) 10
> ((~λ (x y ($ (A a b))) (+ x y a b)) 10 20 (A 30 40)) 100
> (~λ ((~vs v1 v2)) (+ v1 v2)) eval:79.0: ~λ: expected a generic bind instance
at: ((~vs v1 v2))
in: (~λ ((~vs v1 v2)) (+ v1 v2))
parsing context:
while parsing a generic bind instance for define contexts
term: ((~vs v1 v2))
location: eval:79.0
> ((~λ ([($: x xs) (list 1 2)]) (+ x (length xs) 20))) 22
> ((~λ ([($: x xs) (list 1 2)]) (+ x (length xs) 20)) (list 1 2 3 4)) 24
> ((~λ (#:C ($: x xs)) (append xs (list x))) #:C (list 1 2 3)) '(2 3 1)
> ((~λ (#:D [($: x xs) (list 10 20 30)]) (append xs (list x)))) '(20 30 10)
> ((~λ (#:D [($: x xs) (list 10 20 30)]) (append xs (list x))) #:D (list 1 2 3)) '(2 3 1)
syntax
(~case-lambda clause ...)
clause = (header body ...)
Examples: | ||||||||||||||
|
syntax
(~case-lam clause ...)
syntax
(~case-define f clause1 ...)
(~case-define f clause2 ...)
clause1 = (header body ...) clause2 = (b ... → body ...)
Examples: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
syntax
(~case-def clause ...)
Examples: | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
syntax
(~let loop ([db e] ...) body ...)
(~let ([b e] ...) body ...)
db = define-allowable-generic-bind | identifier? b = generic-bind | identifier?
Note that when using the match $ binding with ~let, the behavior is NOT like match-let, in that there is no coupling between between the binding positions. This means that trying to bind duplicate identifiers in a $ match pattern will produce the same results or errors as in let, let*, or letrec.
Examples: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
syntax
(~let* ([b e] ...) body ...)
b = generic-bind | identifier?
Examples: | |||||||||||||||||||
|
syntax
(~letrec ([b e] ...) body ...)
b = generic-bind | identifier?
Examples: | |||||||||||||||||||||||||||||||||
|
3 Generic Comprehension Forms
All the forms in this section are the same as their Racket counterparts (see for), but with added generic bind support.
syntax
(~for ...)
syntax
(~for/list ...)
syntax
(~for/lists ...)
syntax
(~for/vector ...)
syntax
(~for/fold ...)
syntax
(~for/first ...)
syntax
(~for/last ...)
syntax
(~for/or ...)
syntax
(~for/and ...)
syntax
(~for/sum ...)
syntax
(~for/product ...)
syntax
(~for/hash ...)
syntax
(~for/hasheq ...)
syntax
(~for/hasheqv ...)
syntax
(~for* ...)
syntax
(~for*/list ...)
syntax
(~for*/lists ...)
syntax
(~for*/vector ...)
syntax
(~for*/fold ...)
syntax
(~for*/first ...)
syntax
(~for*/last ...)
syntax
(~for*/or ...)
syntax
(~for*/and ...)
syntax
(~for*/sum ...)
syntax
(~for*/product ...)
syntax
(~for*/hash ...)
syntax
(~for*/hasheq ...)
syntax
(~for*/hasheqv ...)
Examples: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
4 Implementing New Generic Binding Instances
Only defining match-specific new binding instances are currently possible.
syntax
(define-match-bind (name x ...))
(define-match-bind name)
> (struct B (x y z))
> (define-match-bind (B x y z))
> (~define (bf ($B x y z)) (+ x y z))
> (bf (B 20 40 60)) 120
> (define-match-bind hash-table)
> (~define ($hash-table [keys vals] ...) (hash 'a 1 'b 2 'c 3))
> keys '(a b c)
> vals '(1 2 3)
syntax
(~struct ...)
> (~struct C (a b c [d #:mutable]))
> (define c (C 9 8 7 6))
> (C? c) #t
> (C-a c) 9
> (C-b c) 8
> (C-c c) 7
> (C-d c) 6
> (set-C-d! c 20)
> (C-d c) 20