On this page:
1.1 Utilities for Macro Definition
define-syntax-parse-rule/  autoptic
define-syntax-parse-rule/  autoptic/  loc
1.2 Utilities for Binding Syntax
autoptic-binds-to
define-simple-normalizing-binder
1.3 Utilities for Functional Programming
1.3.1 Bindings and recursion
pass
w-
fn
w-loop
loopfn
1.3.2 Conditionals
mat
expect
matfns
expectfn
dissect
dissect/  derived
dissectfn
dissectfn/  derived

1 Evergreen Utilities for Binding Syntax and Pure FP🔗ℹ

 (require lathe-comforts) package: lathe-comforts-lib

1.1 Utilities for Macro Definition🔗ℹ

syntax

(define-syntax-parse-rule/autoptic (id pattern ...)
  pattern-directive ...
  template)
Defines a syntax transformer named id similarly to define-syntax-parse-rule, except that it first verifies that that the literal list (id pattern ...) at the call site has scopes that are a subset of the scopes on that list’s first cons cell.

Defining a syntax transformer this way helps to ensure autopticity of each of its call sites. Note that each of the patterns is still the usual sort of syntax-parse pattern, and it won’t ensure autopticity without the use of helpers like the ~autoptic-list pattern expander.

This syntax itself must also be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

syntax

(define-syntax-parse-rule/autoptic/loc (id pattern ...)
  pattern-directive ...
  template)
Defines a syntax transformer named id similarly to define-syntax-parse-rule, except that it first verifies that that all the tails of the literal list (id pattern ...) at the call site have scopes that are a subset of the scopes on that list’s first cons cell, and it restores the list’s source location on the transformed code as though using syntax/loc.

Defining a syntax transformer this way helps to ensure autopticity of each of its call sites. Note that each of the patterns is still the usual sort of syntax-parse pattern, and it won’t ensure autopticity without the use of helpers like the ~autoptic-list pattern expander.

This syntax itself must also be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

1.2 Utilities for Binding Syntax🔗ℹ

splicing syntax class

(autoptic-binds-to surrounding-stx)  splicing syntax class

  surrounding-stx : syntax?
Matches syntax in any of three formats:

  • 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.

(See _, expr, and id.)

syntax

(define-simple-normalizing-binder (id pattern ...)
  (template ...))
Defines a syntax transformer named id which "normalizes" its binding syntax. Its input is a form that uses the rather permissive binds splicing syntax class, and its output specifically uses the ([var val] ...) binding format expected by most Racket binding syntaxes.

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-syntax-parse-rule/autoptic/loc
  (id pattern ... vars body:expr ...)
  #:declare vars (autoptic-binds-to this-syntax)
  (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))

Defining a syntax transformer this way helps to ensure autopticity of each of its call sites. Note that each of the patterns is still the usual sort of syntax-parse pattern, and it won’t ensure autopticity without the use of helpers like the ~autoptic-list pattern expander.

This syntax itself must also be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

1.3 Utilities for Functional Programming🔗ℹ

1.3.1 Bindings and recursion🔗ℹ

procedure

(pass arg func)  any

  arg : any/c
  func : (-> any/c any)
Invokes the given procedure with the given argument value. In other words, (pass arg func) is just like (func arg) but in a different order.

This utility can come in handy when experimenting with a new operation that returns procedures—for example, match-lambda. Instead of going to the trouble to define another operation that acts as a let binding—in this case, matchit’s easy enough to use pass and a weak bracket to accomplish basically the same programming style:

> (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 ...)

Works just like a let form with no proc-id, but uses the autoptic-binds-to splicing syntax class for the syntax of its bindings, so parentheses can usually be omitted.

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

Examples:
> (w- ([a 1] [b 2])
    (+ a b))

3

> (w- [a 1 b 2]
    (+ a b))

3

> (w- a 1 b 2
    (+ a b))

3

syntax

(fn arg-id ... body-expr)

Creates a procedure with positional arguments arg-id ... and body body-expr.

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

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.

Examples:
> (pd / hash-map (hash 'a 1 'b 2) / fn k v
    (format "(~s, ~s)" k v))

'("(b, 2)" "(a, 1)")

> (pd / build-list 5 / fn ~ / * 10 ~)

'(0 10 20 30 40)

syntax

(w-loop proc-id local-binds body ...)

Works just like a let form with a proc-id, but uses the autoptic-binds-to splicing syntax class for the syntax of its bindings, so parentheses can usually be omitted.

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)

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

syntax

(loopfn proc-id arg-id ... body-expr)

Creates a procedure with positional arguments arg-id ... and body body-expr, which can refer to itself in the body using the name proc-id.

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

1.3.2 Conditionals🔗ℹ

syntax

(mat val-expr pat then-expr else-expr)

syntax

(expect val-expr pat else-expr then-expr)

Checks whether pat matches the result of val-expr. If it does, this evaluates then-expr in tail position with the bindings introduced by pat. Otherwise, this evaluates else-expr without any new bindings.

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

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.

Examples:
> (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

(matfns pat then-expr elsefn-expr)

 
  elsefn-expr : (-> any/c any)
Returns a procedure. The procedure takes a single argument value and checks whether it matches the match pattern pat. If it does, the procedure evaluates then-expr in tail position with the bindings introduced by pat. Otherwise, the procedure makes a tail call to the procedure resulting from elsefn-expr, passing in the same argument value.

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

syntax

(expectfn pat else-expr then-expr)

Returns a procedure. The procedure takes a single argument value and checks whether it matches the match pattern pat. If it does, the procedure evaluates then-expr in tail position with the bindings introduced by pat. Otherwise, the procedure evaluates else-expr in tail position without any new bindings.

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

syntax

(dissect val-expr pat then-expr)

syntax

(dissect/derived orig val-expr pat then-expr)

Checks whether pat matches the result of val-expr. If it does, this evaluates then-expr in tail position with the bindings introduced by pat. Otherwise, the exn:misc:match? exception is raised.

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

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)

Returns a procedure. The procedure takes a single argument value and checks whether it matches the match pattern pat. If it does, the procedure evaluates then-expr in tail position with the bindings introduced by pat. Otherwise, the exn:misc:match? exception is raised.

This syntax must be called with autopticity. An occurrence of a cons cell that’s part of the call syntax must have a set of scopes that’s equal to or a superset of the set of scopes on the entire call, as though the call has created a local binding of what a cons cell means in these positions. This helps ensure that even though Racket expressions are often made of cons cells, an expression inserted into one of these positions by a macro’s syntax template will not have its cons cells misinterpreted.

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.