Lathe Comforts for Racket is a collection of utilities that are handy for writing Racket code. This is a non-intrusive toolkit; in most cases it should only make certain Racket code easier to write, without substantially changing the architecture of the project it’s used in.
Some of these utilities are designed with Parendown in mind. In some cases, Parendown’s weak opening brackets make it easier to get by with higher-order functions instead of custom syntax. (Note that due to limitations of Scribble’s Racket code formatter, we use Parendown’s ‘pd‘ macro to achieve these weak parens, rather than using its custom reader syntax.)
|(require lathe-comforts)||package: lathe-comforts-lib|
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.
(define-simple-normalizing-binder (id pattern ...) (template ...))
(define-simple-macro (id pattern ... vars:binds body:expr ...) (template ... ([vars.var vars.val] ...) body ...))
(define-simple-normalizing-binder (w-) (let)) (define-simple-normalizing-binder (w-loop proc:id) (let proc))
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])
> (pd / pass (list 1 2 3) / match-lambda [(list) #f] [(cons first rest) rest])
(w- binds body-expr ...)
(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.
(w-loop proc-id 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)
(loopfn proc-id arg-id ... body-expr)
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.
(expectfn pat else-expr then-expr)
(dissect val-expr pat then-expr)
If you need a custom error message, use expect with an expression that raises an exeption.
(dissectfn pat then-expr)
If you need a custom error message, use expectfn with an expression that raises an exeption.
Maybe values are a way to encode optional data. Using maybe values can simplify some interfaces that would otherwise use run time errors or special-cased sentinel values like #f.
(struct nothing ())
Every two nothing values are equal?.
Some values never really vary at all. Perhaps some library accepts an argument that it’ll pass through, but the library’s client has no need for its pass-through services this time. Perhaps some data structure can store annotations on certain nodes, but the client doesn’t really care to annotate any of the nodes this time. In cases like these, it’s useful to have a particular value that doesn’t mean anything.
Racket programs sometimes use (void) for this purpose, but that value is more commonly used as the return value of side-effecting operations which will never have a meaningful result to print at the top level. If a user exploring at the top level uses an operation that typically returns a pass-through value or label, but in this case it happens to return a trivial pass-through value or a trivial label, that’s potentially interesting information for the user, since they may not have even known they were dealing with trivial data yet.
So Lathe Comforts provides a very simple structure type, trivial, to represent trivial values.
Every two trivial values are equal?.