← back to index

Program Logics via Distributive Monoidal Categories

Bonchi, Di Lavore, RomΓ‘n, Staton Β· 2025 Β· arxiv arXiv:2507.18238

Every Hoare rule you learned in your algorithms course β€” SKIP, COMP, WHILE, ASSIGN β€” is a theorem, not an axiom, once your category has the right structure.

The Hoare triple

You already know this pattern:

P c Q if P holds before, Q holds after
assert P; run(c); assert Q

"If P holds before running c, then Q holds after." That's a wpHoare triple: {P} c {Q}. If the precondition doesn't hold, the triple says nothing β€” it's vacuously satisfied.

Python

SKIP β€” doing nothing preserves anything

P P no box β€” wire passes straight through

The simplest rule. {P} skip {P}. If P is true before doing nothing, P is still true after. Obvious β€” but it's a theorem, not an assumption.

Python

COMP β€” the handshake

This is the big one. If {P} c₁ {R} and {R} cβ‚‚ {Q}, then {P} c₁;cβ‚‚ {Q}.

R is the handshake β€” stage 1's postcondition is stage 2's precondition. The output guarantee of one stage is the input assumption of the next.

P c₁ R c₂ Q {P} c₁;c₂ {Q}
Python

WHILE β€” loop invariants

P b? true c P survives each iteration false P ∧ ¬b

If {b ∧ P} c {P}, then {P} while b do c {P ∧ ¬b}.

If the body preserves the invariant P, the loop preserves it too. When the loop exits, P still holds AND the guard b is false. This is the loop invariant from your algorithms class β€” except here it's a theorem derived from the categorical structure, not a proof technique you choose to apply.

Python

Weakest precondition

Q c wp(c, Q) = ? given Q and c, compute the weakest P

Flip the question. Given c and Q, what's the weakest P that guarantees Q? That's wp(c, Q). The most permissive input condition that still gives you the output you want.

Python

Copy and discard β€” the structure underneath

An wpimperative category has two special operations per type: copy (Ξ½) and discard (Ξ΅). A morphism is deterministic if it commutes with copy. This is how Fritz (2020) distinguishes deterministic from stochastic morphisms.

Python

The bridge β€” contracts ARE Hoare triples

The Lean artifact proves the equivalence:

-- Lean 4
theorem contract_is_triple :
  ContractPreserving f Q ↔ Triple (fun _ => True) f Q

ContractPreserving f Q says: for every input, every output in the support of f satisfies Q. That's {⊀} f {Q} β€” a Hoare triple where the precondition is "true" (anything goes). The contract is an unconditional guarantee.

Python
Lean proof
theorem contract_is_triple [Monad M] [Support M]
    {f : Kernel M Ξ± Ξ²} {Q : Contract Ξ²}
    : ContractPreserving f Q ↔ Triple (fun _ => True) f Q

ghBridge.lean

Confidence: Exact.

The punchline β€” Corollary 76

Stoch (stochastic matrices) is a posetal imperative category. That means every Hoare rule in Section 5 holds there as a theorem. Hoare triples aren't an engineering choice β€” they're forced by the category.

A stochastic function takes an input and returns a probability distribution over outputs. wpKleisli composition chains two such functions: run the first, then for each possible output run the second, weighting by probability. This is how stochastic pipelines compose β€” and it's the composition that makes the COMP rule work.

Python
Lean proof
theorem forward_triple [Monad M] [LawfulMonad M] [Support M]
    {I : InterfaceTypes} {p : Pipeline M I}
    (t : PipelineTriples M I p) (policy : I.policy)
    : Triple t.P_raw (p.forwardKernel policy) t.Q_ranked :=
  four_stage_comp t.h_perceive t.h_cache t.h_filter (t.h_attend policy)

ghComp.lean

Confidence: Simplified. Finite dicts, not measurable spaces. Same composition law.

Notation reference

Paper Python Meaning
{P} c {Q}assert P; c(); assert QHoare triple
c₁ ; cβ‚‚c2(c1(x))Sequential composition
⊀lambda _: TrueAlways true
βŠ₯lambda _: FalseAlways false
wp(c, Q)lambda a: all(Q(b) for b in c(a))Weakest precondition
Ξ½lambda x: (x, x)Copy
Ξ΅lambda x: NoneDiscard
≀# refinementOne program refines another
kl(T)# functions returning T(...)Kleisli category
βŠ—(x, y)Tensor (parallel)

Rules reference (Theorem 79)

Every rule is a theorem in any posetal imperative category. Stoch is one (Corollary 76).

Rule Statement Plain English
SKIP{P} skip {P}Doing nothing preserves anything
COMP{P}c₁{R}, {R}cβ‚‚{Q} β‡’ {P}c₁;cβ‚‚{Q}Chain postconditions. The handshake.
WHILE{b∧P} c {P} β‡’ {P} while b do c {P∧¬b}Loop invariant survives
AND{P₁}c{Q₁}, {Pβ‚‚}c{Qβ‚‚} β‡’ {Pβ‚βˆ§Pβ‚‚}c{Qβ‚βˆ§Qβ‚‚}Conjunction of specs is a spec
TOP{P} c {⊀}Anything satisfies "true"
BOT{βŠ₯} c {Q}Impossible precondition β†’ vacuously true
Neighbors

Other paper pages

Foundations (Wikipedia)

Translation notes

All Python examples on this page use finite dicts as probability distributions. The paper works over arbitrary measurable spaces, continuous distributions, and the full subdistribution monad on StdBorel. For example: the WHILE rule on this page counts down from 10 to 0 using integers. In the paper, the same rule applies to a probabilistic loop over continuous state spaces β€” a particle filter updating a belief distribution until convergence. The invariant structure (P survives each iteration) is identical. The state space is not.

The bridge theorem section is tagged Exact β€” the Python computes the same equivalence the Lean proof verifies. Every other example is Simplified.

Framework connection: Stoch is the ambient category for the Natural Framework pipeline; the COMP rule is the handshake between stages. (jkAmbient Category, jkThe Handshake)

Ready for the real thing? arxiv Read the paper (52 pages). Start at Section 5.1 (p.20) for correctness triples, Corollary 76 (p.19) for why Stoch qualifies.