Lectures onType Theory
Chapter 104
Chapter 104Optional

Weakest Preconditions and Dijkstra Monads

Prerequisites. Direct starred prerequisites: Chapter 42. No later core chapter depends on this route.

The type FNat says that a computation may perform effects and return a natural number. It does not distinguish a state computation that increments its cell from one that resets the cell, nor an exception that is impossible from one that is inevitable. A specification must relate the initial state, the returned value, and every terminal outcome. An effect name does not contain that relation.

Postconditions determine preconditions

For a result type A, a postcondition is a predicate p:AU. A total pure computation returning a:A satisfies p exactly when p(a) holds. This forces the first predicate transformer.

Definition 104.1 — Weakest-precondition types

The pure, exception, state, and state-with-exception predicate-transformer types are WPPure(A):=(AU)U,WPExn(A):=(AU)(EU)U,WPSt(A):=((A×S)U)SU,WPStExn(A):=((A×S)U)(E×SU)SU. A transformer w is monotone when pq entails w(p)w(q), with one implication for each postcondition argument. It is conjunctive when it maps an arbitrary pointwise conjunction of postconditions to the conjunction of their preconditions.

For pure return and sequencing, the types force (R)returnwp(a):=λp.p(a),(B)bindwp(w,k):=λp.w(λa.k(a)(p)). The calculation for two returns is bindwp(returnwp(a),λx.returnwp(f(x)))(p)=(B)returnwp(a)(λx.p(f(x)))=(R)p(f(a)). The two tags name the displayed equations, not rules; the typing rules that carry the same names are introduced in definition 104.5. A postcondition alone is not a precondition until a computation determines how the two are connected.

Definition 104.2 — State and exception operations

For state, return and bind are returnStwp(a)(p)(s):=p(a,s),bindStwp(w,k)(p)(s0):=w(λ(a,s1).k(a)(p)(s1))(s0),getwp(p)(s):=p(s,s),putwp(s)(p)(s):=p(unit,s). For exceptions, returnExnwp(a)(p)(q):=p(a),raisewp(e)(p)(q):=q(e),bindExnwp(w,k)(p)(q):=w(λa.k(a)(p)(q))(q). For state with exceptions, the combined clauses are returnStExnwp(a)(p)(q)(s):=p(a,s),raiseStExnwp(e)(p)(q)(s):=q(e,s),bindStExnwp(w,k)(p)(q)(s0):=w(λ(a,s1).k(a)(p)(q)(s1))(q)(s0),catchStExnwp(w,h)(p)(q)(s0):=w(p)(λ(e,s1).h(e)(p)(q)(s1))(s0). These clauses thread the state through both successful and exceptional postconditions. A semantics that discards the state on an exception is a different transformer and must replace them.

For incr=get()toxinput(x+1), the state calculation is wp(incr)(p)(s0)=definition104.2,bindandgetwp(put(s0+1))(p)(s0)=definition104.2,putp(unit,s0+1). The first step uses two clauses at once: bindSt hands getwp the continuation λ(a,s1).putwp(a+1)(p)(s1), and getwp applies it to (s0,s0). Thus the desired contract λps0.s1.s1>s0p(unit,s1) is discharged by the arithmetic obligation s0+1>s0.

Exercise 104.1

★★☆ Compute the state-with-exception transformer of a program that reads the state, raises negative when it is below zero, and otherwise writes its successor. State the successful and exceptional postconditions and the precondition obtained in each branch.

Dijkstra monads from monads

Writing predicate transformers by hand risks choosing a return and bind that do not satisfy the monad equations. The source calculus DM instead starts with a computational monad and derives its specification by a selective continuation translation.

Definition 104.3 — The selective CPS translation

The DM type grammar distinguishes effect-free arrows from arrows whose codomain is in the abstract monad τ: A::=XbAnAA+AA×A,H::=AC,C::=HτAHnCC×C. Terms are variables, application, typed abstraction, constants, pairs and projections, injections and case analysis, together with returnτeandbindτe1toxine2. The judgments ΔΓe:H!nandΔΓe:A!τ separate pure terms from monadic terms. A monadic term can enter a larger term only as the first premise of bind. The translation () is homomorphic except at τ-arrows: (HτA):=H(AU)U. Its computational clauses are (returnτe)=λp.p(e),(bindτe1toxine2)=λp.e1(λx.e2(p)). The translation is indexed by a typing derivation because the same surface arrow has different behavior at an effect-free or τ codomain.

For the state monad St(A)=Sτ(A×S), the translated type is St(A)=S((A×S)U)U, which is WPSt(A) up to argument order. Translating the monadic state return and bind gives exactly the clauses of definition 104.2.

Theorem 104.4 — CPS produces a Dijkstra monad

For every well-typed DM monad (T,η,bindT):

  1. the translated operations are well typed;

  2. every translated predicate transformer is monotone and conjunctive;

  3. the translation preserves the source equations, so (T,η,bindT) satisfies the three monad laws extensionally.

Proof of Theorem 104.4 — CPS produces a Dijkstra monad

Proof. All three claims are proved by induction on the displayed DM typing derivation. We give the two monadic cases first, since every other case is homomorphic.

For return, the induction hypothesis gives ΔΓe:A. Hence λp.p(e):(AU)U. If pq, application gives p(e)q(e). For a family (pi)iI, beta reduction gives (returnτe)(λa.iI.pi(a))iI.pi(e), which is the required conjunction.

For bind, inversion gives e1:A!τ and e2:A!τ under x:A. By the induction hypotheses, e1 and every substituted e2[a/x] have the indicated predicate-transformer types. Thus λp.e1(λa.e2[a/x](p)):(AU)U. Given pq, monotonicity of each e2[a/x] gives e2[a/x](p)e2[a/x](q); monotonicity of e1 transports that pointwise implication. For conjunctions, conjunctivity of e2[a/x] rewrites the continuation pointwise, and conjunctivity of e1 then distributes the outer transformer. This proves typing, monotonicity, and conjunctivity in the bind case.

For a variable or constant, the translated term has its translated declared type. Abstraction extends Γ by the translated domain and uses the induction hypothesis on the body; application eliminates that arrow. Pairing, projections, injections, and case analysis apply their ordinary typing rules to the induction hypotheses. Their translated predicate transformers use postconditions only through translated subterms, so the preceding pointwise monotonicity and conjunction arguments apply component by component. These constructors and the two monadic forms exhaust the grammar in definition 104.3, completing claims 1 and 2.

For claim 3, induct on an equational derivation. Congruence cases follow from the induction hypotheses and congruence of the target theory. The beta, projection, and case equations translate to the corresponding target equations because the translation is homomorphic on those constructors. The return and bind equations translate by beta reduction. In particular, for all w,k,h and postconditions p, bindwp(returnwp(a),k)(p)k(a)(p),bindwp(w,returnwp)(p)w(p),bindwp(bindwp(w,k),h)(p)bindwp(w,λa.bindwp(k(a),h))(p). The first two equations are beta reduction. Expanding both sides of the third gives w(λa.k(a)(λb.h(b)(p))) on each side. Function extensionality in the target converts these pointwise equations into the three monad laws for T. No constructor outside the displayed grammar occurs in the induction. ◻

For example, target left identity is the annotated calculation bindwp(returnwp(a),k)(p)=(B)returnwp(a)(λx.k(x)(p))=(R)k(a)(p). Monotonicity is not decoration, and one transformer at result type A=Nat shows what it excludes. Take w¬(p):=(p(0)). It is antitone: from pq one gets w¬(q)w¬(p), the implication running the wrong way. To exhibit the failure, choose two postconditions with pq and evaluate both at 0: p(a):=,q(a):=1. Then pq holds, p(0) is false, and q(0) is true. Hence w¬(p)=()is inhabited,w¬(q)=(1)is not, so w¬(p)w¬(q) fails and w¬ is not monotone. Weakening a postcondition should never strengthen the precondition it demands, and here it does. Clause 2 of theorem 104.4 is exactly what keeps w¬ out of the generated interface: no source computation translates to it.

Computation types and verification conditions

Definition 104.5 — Dijkstra computation type

The type MAw classifies computations returning A whose generated weakest precondition is at least as weak as the user transformer w. Subtyping is contravariant in preconditions:

ΓM:MAwΓp.w(p)w(p)
ΓM:MAw
WP-Sub

Return and bind use the generated operations:

ΓV:A
ΓreturnV:MA(returnwpV)
WP-Return
ΓM:MAwΓ,x:AN:MBk(x)
ΓMtoxinN:MB(bindwp(w,k))
WP-Bind

The logical implication in WP-Sub is the generated verification condition: proving it checks the advertised contract.

Three further computation types are needed to state the soundness theorem, and they are not interchangeable. TotA classifies total pure terms of type A, with no specification attached. PureAw is the primitive instance of MAw at the pure monad, whose transformer type is WPPure(A). STAw is the state instance, whose representation is the state-passing pure computation ST.reprAw:=Πs0:S. Pure(A×S)(λp.w(p)(s0)). Two coercions connect them. The reification map, defined after the typing rules, sends e:STAw to reifye:Tot(ST.reprAw). Running drops a specification, and it may do so only when that specification is satisfiable:

Γe:PureAwΓp.w(p)
Γrune:TotA
WP-Run

Its operational root is

run(Pure.returnv)v
R-Run

The compatible closure reduces the argument of run before this root fires. The second premise is what makes Tot honest: it is unconditionally total, so a computation may enter it only after its precondition has been shown inhabited.

Apply WP-Sub to incr. Its inferred transformer is wi(p)(s0)=p(unit,s0+1). For the advertised transformer wa(p)(s0)=s1.s1>s0p(unit,s1), the verification condition is p,s0.wa(p)(s0)p(unit,s0+1), proved by instantiating s1=s0+1 and arithmetic. The solver may discharge that final formula, but the translation and soundness theorem determine why it is the right formula.

Definition 104.6 — Reification

For a user-defined monad T with pure implementation T^, reifyM reveals the implementation of a terminating T-computation as a total term. Reflection packages such a term back at the abstract effect. Reification reduces return and bind by the corresponding operations of T^. It is not a rule for exposing primitive concurrency, divergence, or an arbitrary handler.

Theorem 104.7 — Conditional WP soundness for total computations

Fix an EMF signature accepted by the source well-formed signature judgment. Assume the following metatheoretic package for that signature.

  1. The target CIC is strongly normalizing, and the source’s erasure from EMF to CIC is type preserving and a strict forward simulation: every source step is matched by one or more target steps.

  2. Source reduction preserves computation types.

  3. If a closed normal term has type PureAw, it is Pure.returnv for some value v:A, and inversion of its computation type gives q:AU.w(q)q(v).

If e:PureAw,p:AU,w(p), then runev for a value v:A satisfying p(v); the premise w(p) also discharges the satisfiability side condition of WP-Run. For state, if e:STAw, s0:S, and w(p)(s0), then run((reifye)s0) reduces to a pair (v,s1):A×S satisfying p(v,s1).

Proof of Theorem 104.7 — Conditional WP soundness for total computations

Proof. Suppose an infinite source reduction began at e. Strict forward simulation would concatenate its nonempty target segments into an infinite CIC reduction, contradicting clause 1. Hence e has a normal form n. Clause 2 gives n:PureAw. By clause 3, choose v:A such that n=Pure.returnv,q:AU.w(q)q(v). Specialize the second formula to p and use the premise w(p); this gives p(v). The source rule R-Run removes the normal-form return, so runev.

For the state clause, the displayed state representation and reification rule give (reifye)s0:Pure(A×S)(λp.w(p)(s0)). Apply the pure clause with postcondition p:(A×S)U and premise w(p)(s0). The resulting value is a pair (v,s1):A×S, and p(v,s1) holds. ◻

The passage from a closed normal form to a returned value uses clause 3 above. Consequently, the theorem records forward simulation and the canonical-return property as hypotheses rather than deriving them from the transformer laws.

Removing totality invalidates the step from normalization to a returned value. Removing monotonicity invalidates sequencing under a stronger postcondition. Removing the well-formed signature condition permits a claimed action whose implementation and transformer disagree. The theorem does not cover arbitrary handlers, general recursion, concurrency, or an external solver’s soundness.

Exercise 104.2

★★☆ Give a contract for a state computation that increments twice. Derive its transformer by two uses of WP-Bind, then write and prove the exact verification condition required by WP-Sub.

Suggested first pass.

None of these problems is a prerequisite for a later chapter. Begin with exercise 104.3, then complete exercise 104.5.

Exercise 104.3

★★☆ Prove right identity and associativity for bindStwp by annotated extensional calculations. Introduce every state and postcondition before use.

Exercise 104.4

★★★ Define two state-with-exception semantics, one preserving the state on failure and one rolling it back. Calculate one program on both. Show that their transformers disagree on a postcondition that inspects the exceptional state, so no theorem transfers without a rule delta.

Exercise 104.5

★★★ Practical project.dijkstra-vc-generator Implement in Kappa a predicate-transformer interpreter for return, bind, get, put, raise, and catch over finite integer states. Maintain monotonicity by constructing transformers only from the displayed clauses. The named acceptance cases are increment  vc valid,negative-raise  exception post valid,bad-advertised-post  vc rejected. Require also the exact lines bind threads updated state,catch handles the raised computation,put changes the state seen by get. A mutation that resumes the bind body in the original state must fail increment vc valid and the first and third lines above while the checker itself remains well typed. Explain which occurrence of the intermediate state in bindStwp each failure exercises. The program decides finite verification conditions; it does not prove theorem 104.7 or justify an SMT solver.

Sources. The DM grammar, selective CPS, generated Dijkstra monads, EMF calculus, and total-correctness result follow Ahman et al. [AHM^+17]. The dependent-CBPV interface of chapter 42 motivates sequencing, but no theorem there is used as a substitute for the predicate-transformer proof. The selective-CPS proof in Appendix A.2 occupies printed pp. 19–21, so its induction is incorporated in theorem 104.4 rather than imported.

Search the book

Type to search the local edition.