Closure Operators as API Contracts
Think of every endpoint as computing a closure: a generator produces candidates, a law L is applied until stable, and the fixed point is returned. Declaring “Endpoint : Generator → FixedPoint<S, L>” tells clients the input/output shape and guarantees:
- Soundness: output is closed under L (apply L again, no change).
- Minimality: nothing extra beyond what L forces from the seed.
- Monotonicity: larger seeds yield larger (never weaker) fixed points, enabling incremental recompute/cache.
- Idempotence: re-run returns the same fixed point.
Reusable template:
- Name: e.g.,
transitive-closure - Law L: “extend by relation R until stable”
- Input (Generator): seed set S
- Output (Fixed Point): least X ⊇ S with “if a R b and a ∈ X then b ∈ X”
- Properties: idempotent, monotone, inflationary, least fixed point
- Observables: iterations to convergence, per-step delta (for SSE), stable hash for caching.
Examples:
- Graph reachability: seed nodes → follow edges to saturation.
- Document normalization: raw doc → apply rewrites until stable.
- Authorization derivation: asserted roles → expand via inheritance until stable.
Verification is light: check idempotence, monotonicity, minimality (remove any element and closure breaks). Composition becomes mechanical: closures over compatible laws compose; distributing laws can swap order safely, turning pipelines into algebra you can rearrange without changing meaning.