aontu

Name a reusable constraint

Build a `uint8`/`port` vocabulary as a `type()`-marked block of ordinary fields.

Rendered from docs/how-to/name-a-reusable-constraint.md in the engine repository, where a correction belongs, and where the test suite executes every example on this page.

aontu has no uint8, int8 or port keyword, and does not need one: a constraint is a value, so a name for one is a field. A type()-marked block gives you a library of them that unifies like everything else and emits nothing:

type: type({})

type: {
  uint8: integer & min(0) & max(255)
  int8: integer & min(-128) & max(127)
  port: integer & min(1) & max(65535)
}

listen: $.type.port
listen: 8080
{ "listen": 8080 }

The block is absent from the output and present for unification, and the key name is not reserved: type here is a field that happens to be type()-marked, so defs or schema reads the same.

An out-of-range value is refused at the field that holds it. Write the same document with listen: 70000 as types.aon:

type: type({})

type: {
  uint8: integer & min(0) & max(255)
  int8: integer & min(-128) & max(127)
  port: integer & min(1) & max(65535)
}

listen: $.type.port
listen: 70000
$ aontu types.aon
[aontu/constraint]: Cannot unify values at path $.listen
...
 Cannot unify value: 70000 with value: integer&min(1)&max(65535)
...
$ echo $?
1

The refusal states the normalised residual the value had to satisfy, not the alias’s name: the constraint travelled, the label did not.

Lead with the kind. min(0) & max(255) alone bounds a number, so 1.5 satisfies it:

loose: type({})
loose: byteish: min(0) & max(255)
a: $.loose.byteish
a: 1.5
{ "a": 1.5 }

A sized integer is integer & min(0) & max(255), which is why every alias above starts with integer.

Aliases are ordinary values, so they compose: one can be defined in terms of another, and a use site can narrow one further ($.type.uint8 & max(15)). For a file-local name that skips the path entirely, %port = integer & min(1) & max(65535) declares an alias used as bare %port.

The idiom is specified in Named constraint aliases; the type() mark’s mechanics are the subject of keep schema out of the output.