1 Evergreen Utilities for Binding Syntax and Pure FP
(require lathe-comforts) | package: lathe-comforts-lib |
1.1 Utilities for Binding Syntax
splicing syntax class
A single syntax object which is a list of two-element lists which each contain an identifier and an expression. This uses the pattern ([var:id val:expr] ...), and the user writes something like (w- ([a 1] [b 2]) (+ a b)) when they use this format.
A single syntax object which is a list of an even number of elements which alternate between identifiers and expressions. This uses the pattern [(~seq var:id val:expr) ...], and the user writes something like (w- [a 1 b 2] (+ a b)) when they use this format.
An even number of syntax objects alternating between identifiers and expressions, proceeding as far as possible. This uses the head pattern (~seq (~seq var:id val:expr) ... (~peek-not (~seq _:id _:expr))), and the user writes something like (w- a 1 b 2 (+ a b)) when they use this format.
In all cases, this binds two attributes of ellipsis depth 1, namely var and val, and they carry the same number of matches.
syntax
(define-simple-normalizing-binder (id pattern ...) (template ...))
Specifically, the generated macro is equivalent to the following, where pattern ... and template ... are expanded right away, and the rest of the ellipses are part of the generated macro:
(define-simple-macro (id pattern ... vars:binds body:expr ...) (template ... ([vars.var vars.val] ...) body ...))
(See expr.)
As an example, w- and w-loop are defined straightforwardly in terms of let:
(define-simple-normalizing-binder (w-) (let)) (define-simple-normalizing-binder (w-loop proc:id) (let proc))
1.2 Utilities for Functional Programming
1.2.1 Bindings and recursion
This utility can come in handy when experimenting with a new operation that returns procedures—
> (match (list 1 2 3) [(list) #f] [(cons first rest) rest]) '(2 3)
> (pd / pass (list 1 2 3) / match-lambda [(list) #f] [(cons first rest) rest]) '(2 3)
syntax
(w- local-binds body-expr ...)
syntax
(fn arg-id ... body-expr)
This is only a frequently useful shorthand, not a full replacement of lambda. Unlike lambda, fn can only be used to create functions of fixed arity, with no keyword arguments, and the body may only consist of one expression (although this expression may be a begin form of course). Hence, programs that use fn may still need to use lambda on occasion.
> (pd / hash-map (hash 'a 1 'b 2) / fn k v (format "(~s, ~s)" k v)) '("(a, 1)" "(b, 2)")
> (pd / build-list 5 / fn ~ / * 10 ~) '(0 10 20 30 40)
syntax
(w-loop proc-id local-binds body ...)
This example reverses and squares the numbers in a list, using the next procedure to continue the loop:
> (pd / w-loop next original (list 1 2 3) result (list) (expect original (cons first rest) result / next rest / cons (* first first) result)) '(9 4 1)
syntax
(loopfn proc-id arg-id ... body-expr)
1.2.2 Conditionals
The only difference between mat and expect is the order of then-expr and else-expr in the form. When these are used with Parendown’s weak opening brackets, they enable a programming style where run time error checking and other early exit conditions are kept toward the top of a procedure body, without affecting the indentation of the procedure’s main logic.
> (pd / define (rev lst) (w-loop next lst lst result (list) (mat lst (list) result / expect lst (cons first rest) (error "Expected a list") / next rest / cons first result))) > (rev (list 1 2 3)) '(3 2 1)
> (rev 3) Expected a list
syntax
(expectfn pat else-expr then-expr)
syntax
(dissect val-expr pat then-expr)
syntax
(dissect/derived orig val-expr pat then-expr)
The dissect/derived variant reports errors in terms of orig.
If you need a custom error message, use expect with an expression that raises an exeption.
syntax
(dissectfn pat then-expr)
syntax
(dissectfn/derived orig pat then-expr)
The dissectfn/derived variant reports errors in terms of orig.
If you need a custom error message, use expectfn with an expression that raises an exeption.