let/  assert
1 Binding with assertions
let/  assert
2 FFI-style example
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.900

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]
Evaluates the bindings from left to right and then evaluates the body. Later bindings may refer to earlier bindings, as with let*.

The implementation expands through an internal helper into nested let and cond forms. There is no exception-based control flow in this version: a failing assertion directly selects the fallback result for that binding.

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, binds the result to id, and then calls predicate-expr with that value. If the predicate returns a true value, evaluation continues with the next binding or with the body. If the predicate returns #f, the body is not evaluated and the complete let/assert form returns fallback-expr.

The fallback expression belongs to the binding where the assertion is made. This keeps failure handling close to the operation that may fail.

(let/assert ([x 10 (a->? 0) 'too-small]
             [y (+ x 2) (a-=? 12) 'wrong-value])
  y)

This expression returns 12. The second binding may use x, because the bindings are evaluated sequentially.

(let/assert ([ptr (open-native-handle) a-!nullptr? 'open-failed]
             [ret (use-native-handle ptr) (a->=? 0) 'call-failed])
  'ok)

This style is useful around FFI code. If open-native-handle returns #f, the result is 'open-failed. If use-native-handle returns a negative status code, the result is 'call-failed. Otherwise the body is evaluated and the result is 'ok.

2 FFI-style example🔗ℹ

Many C libraries, including FFmpeg-style APIs, report failure through null pointers or integer return codes. A typical wrapper can therefore keep the normal path small:

(define (open-decoder file)
  (let/assert ([ctx (avformat-open-input file)
                    a-!nullptr?
                    'open-input-failed]
               [ret (avformat-find-stream-info ctx)
                    (a->=? 0)
                    'stream-info-failed]
               [stream (find-best-audio-stream ctx)
                       a-!nullptr?
                       'no-audio-stream])
    stream))

The example is intentionally written as wrapper-style Racket code rather than as a direct FFmpeg binding. The important point is the shape: pointer-returning operations are checked with a-!nullptr?, while return-code operations are checked with predicates such as (a->=? 0).

3 Creating assertion factories🔗ℹ

syntax

(make-assert name not-name pred)

Defines two assertion factories.

The form (name constant) produces a unary predicate that applies pred to the checked value and constant:

(lambda (x) (pred x constant))

The form (not-name constant) produces the negated variant:

(lambda (x) (not (pred x constant)))

For example:

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

defines a-eq? and a-!eq?.

4 Assertion factories🔗ℹ

syntax

(a-eq? constant)

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

syntax

(a-!eq? constant)

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

syntax

(a->? constant)

Produces a predicate that accepts values greater than constant.

syntax

(a-<=? constant)

Produces a predicate that accepts values for which (> value constant) is false.

syntax

(a->=? constant)

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

syntax

(a-<? constant)

Produces a predicate that accepts values for which (>= value constant) is false.

syntax

(a-=? constant)

Produces a predicate that accepts values numerically equal to constant.

syntax

(a-!=? constant)

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

The negated factories are exact negations of their corresponding predicate. For ordinary numeric values, (a-<=? n) behaves like a less-than-or-equal check and (a-<? n) behaves like a less-than check, but they are implemented as negations of > and >=.

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.

procedure

(a-true? x)  boolean?

  x : any/c
Returns #t when x is eq? to #t.

procedure

(a-false? x)  boolean?

  x : any/c
Returns #t when x is eq? to #f.

procedure

(a-nullptr? x)  boolean?

  x : any/c
Returns #t when x is eq? to #f. The name is intended for FFI code where #f represents a null pointer.

procedure

(a-!nullptr? x)  boolean?

  x : any/c
Returns #t when x is not eq? to #f. This is often the success predicate for FFI calls that return either a native pointer or #f.