let-assert
1 Binding with assertions
let/  assert
2 FFI-style examples
3 Creating assertion factories
make-assert
4 Assertion factories
a-eq?
a-!eq?
a->?
a-<=?
a->=?
a-<?
a-=?
a-!=?
5 Ready-made predicates
a-=0?
a-!=0?
a->0?
a->=0?
a-<0?
a-<=0?
a-true?
a-false?
a-nullptr?
a-!nullptr?
9.1

let-assert🔗ℹ

Hans Dijkema <hans@dijkewijk.nl>

 (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]
The form expands to an internal let* structure. Bindings are therefore evaluated from left to right, and later bindings may refer to earlier ones.

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)

Defines two assertion factories in a definition context.

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:

(make-assert a-eq? a-!eq? eq?)

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)

Produces a unary predicate that accepts values for which (eq? value constant) is true.

syntax

(a-!eq? constant)

Produces a unary predicate that accepts values for which (eq? value constant) is false.

syntax

(a->? constant)

Produces a unary predicate that accepts values greater than constant.

syntax

(a-<=? constant)

Produces a unary predicate that rejects values greater than constant.

syntax

(a->=? constant)

Produces a unary predicate that accepts values greater than or equal to constant.

syntax

(a-<? constant)

Produces a unary predicate that rejects values greater than or equal to constant.

syntax

(a-=? constant)

Produces a unary predicate that accepts values numerically equal to constant.

syntax

(a-!=? constant)

Produces a unary predicate that accepts values not numerically equal to constant.

5 Ready-made predicates🔗ℹ

procedure

(a-=0? x)  boolean?

  x : number?
Returns #t when x is numerically equal to zero.

procedure

(a-!=0? x)  boolean?

  x : number?
Returns #t when x is not numerically equal to zero.

procedure

(a->0? x)  boolean?

  x : real?
Returns #t when x is greater than zero.

procedure

(a->=0? x)  boolean?

  x : real?
Returns #t when x is greater than or equal to zero.

procedure

(a-<0? x)  boolean?

  x : real?
Returns #t when x is less than zero.

procedure

(a-<=0? x)  boolean?

  x : real?
Returns #t when x is less than or equal to zero.

A predicate equivalent to (a-eq? #t).

A predicate equivalent to (a-eq? #f).

A predicate equivalent to (a-eq? #f). The name is intended for FFI code where #f is used to represent a null pointer.

A predicate equivalent to (a-!eq? #f). It accepts values that are not #f, which is often the success case for pointer-returning FFI calls.