Program Logics via Distributive Monoidal Categories
Bonchi, Di Lavore, RomΓ‘n, Staton Β· 2025 Β·
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:
"If P holds before running c, then Q holds after." That's a Hoare triple:
{P} c {Q}. If the precondition doesn't hold, the triple says nothing β it's vacuously satisfied.
SKIP β doing nothing preserves anything
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.
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.
WHILE β loop invariants
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.
Weakest precondition
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.
Copy and discard β the structure underneath
An imperative 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.
The bridge β contracts ARE Hoare triples
The Lean artifact proves the equivalence:
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.
Lean proof
theorem contract_is_triple [Monad M] [Support M]
{f : Kernel M Ξ± Ξ²} {Q : Contract Ξ²}
: ContractPreserving f Q β Triple (fun _ => True) f Q 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. Kleisli 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.
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) Confidence: Simplified. Finite dicts, not measurable spaces. Same composition law.
Notation reference
| Paper | Python | Meaning |
|---|---|---|
| {P} c {Q} | assert P; c(); assert Q | Hoare triple |
| cβ ; cβ | c2(c1(x)) | Sequential composition |
| β€ | lambda _: True | Always true |
| β₯ | lambda _: False | Always false |
| wp(c, Q) | lambda a: all(Q(b) for b in c(a)) | Weakest precondition |
| Ξ½ | lambda x: (x, x) | Copy |
| Ξ΅ | lambda x: None | Discard |
| β€ | # refinement | One 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
- π Fritz 2020 β the category (FinStoch) where these rules hold
- π Gaboardi 2021 β Hoare triples with grades (track side effects)
- π Kura 2026 β the restricted pointwise order (handshake as graded monad)
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. (
Ambient Category,
The Handshake)
Read the paper (52 pages). Start at Section 5.1 (p.20) for correctness triples, Corollary 76 (p.19) for why Stoch qualifies.