let-assert
| (require let-assert) | package: let-assert |
This module provides let/assert, a small sequential binding form with local assertions. It is useful for defensive programming around FFI bindings: checks for null pointers, exit codes, and similar failure values can be kept close to the binding that produced them, while the body stays on the happy path. When an assertion fails, the whole let/assert expression returns the associated fallback value.
1 Binding with assertions
syntax
(let/assert (binding ...) body ...+)
binding = [id expr] | [id expr predicate-expr fallback-expr]
A binding of the form [id expr] simply binds id to expr.
A binding of the form [id expr predicate-expr fallback-expr] evaluates expr once, applies predicate-expr to the result, and binds id to that result when the predicate accepts it. If the predicate returns #f, the body is not evaluated and the whole let/assert form returns fallback-expr.
The fallback expression is evaluated only when the assertion fails. Internally, the failing assertion raises a private exception carrying that value; the exception is caught by let/assert itself.
(let/assert ([x 10 (a->? 0) 'too-small] [y (+ x 2) (a-=? 12) 'wrong-value]) y)
The example returns 12. The second binding can use x, because let/assert has let* scoping.
2 FFI-style examples
FFI libraries often report errors by returning a null pointer or a negative integer status code. With let/assert, those checks can be written next to the operation that may fail.
(define (open-ffmpeg-instance) (let/assert ([fh (fmpg-init) a-!nullptr? #f]) fh))
Here fmpg-init is expected to return either a valid native handle or #f. If the handle is #f, the complete let/assert form returns #f. Otherwise the valid handle is returned.
A slightly larger example can combine a pointer check with an FFmpeg-style return-code check:
(define (decode-next! ds) (let/assert ([pkt (av-packet-alloc) a-!nullptr? 'packet-allocation-failed] [ret (read-selected-audio-packet! ds pkt) (a->=? 0) 'read-packet-failed] [pcm (receive-available-frames! ds) bytes? 'decode-failed]) pcm))
The body contains only the successful path. If packet allocation fails, the result is 'packet-allocation-failed. If reading the packet returns a negative status code, the result is 'read-packet-failed. If decoding does not produce bytes, the result is 'decode-failed.
3 Creating assertion factories
syntax
(make-assert name not-name pred)
The form (name constant) expands to a unary predicate that applies pred to the value being checked and constant. The form (not-name constant) expands to the negated variant.
For example:
defines a-eq? and a-!eq?. Consequently, (a-eq? #f) produces a predicate that accepts values that are eq? to #f, while (a-!eq? #f) accepts values that are not eq? to #f.
4 Assertion factories
syntax
(a-eq? constant)
syntax
(a-!eq? constant)
syntax
(a->? constant)
syntax
(a-<=? constant)
syntax
(a->=? constant)
syntax
(a-<? constant)
syntax
(a-=? constant)
syntax
(a-!=? constant)
5 Ready-made predicates
value
value
value
value