|
| 1 | +/- |
| 2 | +Copyright (c) 2025 Tanner Duve (Logical Intelligence). All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Tanner Duve |
| 5 | +-/ |
| 6 | + |
| 7 | +module |
| 8 | + |
| 9 | +public import Cslib.Foundations.Control.Monad.Free |
| 10 | +public import Cslib.Foundations.Control.Monad.Free.Effects |
| 11 | +public import Std.Do.PredTrans |
| 12 | +public import Std.Do.WP.Basic |
| 13 | +public import Std.Do.WP.Monad |
| 14 | +public import Std.Do.Triple |
| 15 | + |
| 16 | +/-! |
| 17 | +# Weakest preconditions for `FreeM` programs |
| 18 | +
|
| 19 | +Weakest-precondition interpretation of `FreeM F` programs through `Std.Do`'s |
| 20 | +predicate-transformer monad `PredTrans ps`. The universal property of `FreeM` lifts any |
| 21 | +effect handler `F ι → PredTrans ps ι` to a unique monad morphism `wpH H = liftM H.run`, |
| 22 | +so weakest preconditions are compositional in `FreeM`'s monadic structure. An |
| 23 | +`[LHandler F ps]` instance plugs `FreeM F` into `Std.Do`'s `WP`/`WPMonad`/`Triple` |
| 24 | +infrastructure. |
| 25 | +
|
| 26 | +The WP's structural rules (`wpH_pure`, `wpH_bind`, …) are immediate from `liftM` being a monad |
| 27 | +morphism; the adequacy theorem `wpH_ofInterp_eq_wp_liftM` — that WP-via-handler agrees with |
| 28 | +`Std.Do`'s WP of the `liftM` interpretation — is the same statement of uniqueness. |
| 29 | +
|
| 30 | +The design follows [Vistrup, Sammler, Jung. *Program Logics à la Carte.* POPL 2025], adapted |
| 31 | +from coinductive ITrees to inductive `FreeM` and from Iris to `Std.Do`. |
| 32 | +-/ |
| 33 | + |
| 34 | +@[expose] public section |
| 35 | + |
| 36 | +namespace Cslib |
| 37 | + |
| 38 | +open Std.Do |
| 39 | + |
| 40 | +namespace FreeM |
| 41 | + |
| 42 | +universe u v w |
| 43 | + |
| 44 | +variable {F G : Type u → Type v} {ps : PostShape.{u}} {α β : Type u} |
| 45 | + |
| 46 | +/-- A logical effect handler: an effect handler from `F` into the predicate-transformer monad |
| 47 | +`PredTrans ps`. The "logical" qualifier distinguishes these from executable handlers into |
| 48 | +concrete computational monads. Wrapped in a structure to isolate the implicit `ι`-binder from |
| 49 | +class resolution at use sites. -/ |
| 50 | +structure LHandler (F : Type u → Type v) (ps : PostShape.{u}) : Type (max (u + 1) v) where |
| 51 | + /-- The assignment from operations to predicate transformers. -/ |
| 52 | + run {ι : Type u} : F ι → PredTrans ps ι |
| 53 | + |
| 54 | +namespace LHandler |
| 55 | + |
| 56 | +/-- Sum of handlers; the counterpart of the paper's `H₁ ⊕ H₂`. -/ |
| 57 | +def sum (H₁ : LHandler F ps) (H₂ : LHandler G ps) : |
| 58 | + LHandler (fun α => F α ⊕ G α) ps where |
| 59 | + run op := match op with |
| 60 | + | .inl x => H₁.run x |
| 61 | + | .inr y => H₂.run y |
| 62 | + |
| 63 | +@[simp] theorem sum_run_inl (H₁ : LHandler F ps) (H₂ : LHandler G ps) |
| 64 | + {ι : Type u} (x : F ι) : |
| 65 | + (LHandler.sum H₁ H₂).run (Sum.inl x : F ι ⊕ G ι) = H₁.run x := rfl |
| 66 | + |
| 67 | +@[simp] theorem sum_run_inr (H₁ : LHandler F ps) (H₂ : LHandler G ps) |
| 68 | + {ι : Type u} (y : G ι) : |
| 69 | + (LHandler.sum H₁ H₂).run (Sum.inr y : F ι ⊕ G ι) = H₂.run y := rfl |
| 70 | + |
| 71 | +/-- Derive a logical handler from an effect handler into any `[WP m ps]` monad, by composing |
| 72 | +with `m`'s WP. -/ |
| 73 | +def ofInterp {m : Type u → Type w} [WP m ps] |
| 74 | + (interp : ∀ ι : Type u, F ι → m ι) : LHandler F ps where |
| 75 | + run {ι} op := wp (interp ι op) |
| 76 | + |
| 77 | +@[simp] theorem ofInterp_run {m : Type u → Type w} [WP m ps] |
| 78 | + (interp : ∀ ι : Type u, F ι → m ι) {ι : Type u} (op : F ι) : |
| 79 | + (LHandler.ofInterp interp).run op = wp (interp ι op) := rfl |
| 80 | + |
| 81 | +end LHandler |
| 82 | + |
| 83 | +/-- Weakest-precondition interpretation of a `FreeM F α` program against a logical handler `H`. |
| 84 | +Defined as `FreeM.liftM` instantiated at `PredTrans ps`, the unique monad morphism |
| 85 | +`FreeM F → PredTrans ps` extending `H` per the universal property of `FreeM`. -/ |
| 86 | +def wpH (H : LHandler F ps) (x : FreeM F α) : PredTrans ps α := |
| 87 | + x.liftM (fun {_} => H.run) |
| 88 | + |
| 89 | +@[simp] theorem wpH_pure (H : LHandler F ps) (a : α) : |
| 90 | + wpH H (.pure a : FreeM F α) = Pure.pure a := rfl |
| 91 | + |
| 92 | +@[simp] theorem wpH_liftBind (H : LHandler F ps) {ι : Type u} |
| 93 | + (op : F ι) (k : ι → FreeM F α) : |
| 94 | + wpH H (.liftBind op k) = H.run op >>= fun x => wpH H (k x) := rfl |
| 95 | + |
| 96 | +@[simp] theorem wpH_lift (H : LHandler F ps) {ι : Type u} (op : F ι) : |
| 97 | + wpH H (lift op : FreeM F ι) = H.run op := |
| 98 | + liftM_lift _ op |
| 99 | + |
| 100 | +@[simp] theorem wpH_bind (H : LHandler F ps) (x : FreeM F α) (f : α → FreeM F β) : |
| 101 | + wpH H (x >>= f) = wpH H x >>= fun a => wpH H (f a) := |
| 102 | + liftM_bind _ x f |
| 103 | + |
| 104 | +/-- Lift an `F`-operation through the sum handler reduces to running it under `H₁`. -/ |
| 105 | +@[simp] theorem wpH_lift_sum_inl (H₁ : LHandler F ps) (H₂ : LHandler G ps) |
| 106 | + {ι : Type u} (op : F ι) : |
| 107 | + wpH (H₁.sum H₂) (lift (Sum.inl op : F ι ⊕ G ι)) = H₁.run op := by |
| 108 | + simp |
| 109 | + |
| 110 | +/-- Lift a `G`-operation through the sum handler reduces to running it under `H₂`. -/ |
| 111 | +@[simp] theorem wpH_lift_sum_inr (H₁ : LHandler F ps) (H₂ : LHandler G ps) |
| 112 | + {ι : Type u} (op : G ι) : |
| 113 | + wpH (H₁.sum H₂) (lift (Sum.inr op : F ι ⊕ G ι)) = H₂.run op := by |
| 114 | + simp |
| 115 | + |
| 116 | +/-- Adequacy theorem: WP via `FreeM` against an `ofInterp`-derived handler agrees with |
| 117 | +`Std.Do`'s WP of the `liftM` interpretation. Equivalently, two monad morphisms |
| 118 | +`FreeM F → PredTrans ps` extending the same handler are equal. -/ |
| 119 | +theorem wpH_ofInterp_eq_wp_liftM |
| 120 | + {m : Type u → Type w} [Monad m] [LawfulMonad m] [WPMonad m ps] |
| 121 | + (interp : ∀ ι : Type u, F ι → m ι) (x : FreeM F α) : |
| 122 | + wpH (LHandler.ofInterp interp) x = wp (x.liftM (fun {_} => interp _)) := by |
| 123 | + induction x with |
| 124 | + | pure a => simp [wpH, FreeM.liftM, WPMonad.wp_pure] |
| 125 | + | lift_bind op k ih => |
| 126 | + simp [WPMonad.wp_bind, ih] |
| 127 | + |
| 128 | +/-- Records a default logical handler for `F` at shape `ps`, enabling the global |
| 129 | +`WP (FreeM F) ps` instance and any `Triple`/`mvcgen` reasoning over `FreeM F`. -/ |
| 130 | +class HasHandler (F : Type u → Type v) (ps : outParam (PostShape.{u})) where |
| 131 | + handler : LHandler F ps |
| 132 | + |
| 133 | +instance instWPFreeM [HasHandler F ps] : WP (FreeM F) ps where |
| 134 | + wp := wpH HasHandler.handler |
| 135 | + |
| 136 | +instance instWPMonadFreeM [HasHandler F ps] : WPMonad (FreeM F) ps where |
| 137 | + wp_pure _ := rfl |
| 138 | + wp_bind x f := wpH_bind _ x f |
| 139 | + |
| 140 | +/-- Logical handler for the state effect, induced by `Std.Do`'s `WP (StateM σ)`. -/ |
| 141 | +def StateF.handler {σ : Type u} : LHandler (StateF σ) (.arg σ .pure) := |
| 142 | + LHandler.ofInterp (m := StateM σ) (fun _ op => FreeState.stateInterp op) |
| 143 | + |
| 144 | +instance StateF.instHasHandler {σ : Type u} : |
| 145 | + HasHandler (StateF σ) (.arg σ .pure) where |
| 146 | + handler := StateF.handler |
| 147 | + |
| 148 | +/-- WP of a `FreeState` program matches WP of its `StateM` interpretation. -/ |
| 149 | +theorem StateF.wp_FreeState_eq_wp_toStateM {σ : Type u} (comp : FreeState σ α) : |
| 150 | + wp comp = wp (FreeState.toStateM comp) := |
| 151 | + wpH_ofInterp_eq_wp_liftM (m := StateM σ) |
| 152 | + (fun _ op => FreeState.stateInterp op) comp |
| 153 | + |
| 154 | +/-- Hoare spec for `get` on `FreeState`. -/ |
| 155 | +@[spec] |
| 156 | +theorem Spec.get_FreeState {σ : Type u} {Q : PostCond σ (.arg σ .pure)} : |
| 157 | + Triple (MonadStateOf.get : FreeState σ σ) (spred(fun s => Q.1 s s)) Q := by |
| 158 | + mvcgen |
| 159 | + |
| 160 | +/-- Hoare spec for `set` on `FreeState`. -/ |
| 161 | +@[spec] |
| 162 | +theorem Spec.set_FreeState {σ : Type u} (s : σ) {Q : PostCond PUnit (.arg σ .pure)} : |
| 163 | + Triple (MonadStateOf.set s : FreeState σ PUnit) (spred(fun _ => Q.1 ⟨⟩ s)) Q := by |
| 164 | + mvcgen |
| 165 | + |
| 166 | +/-- Logical handler for the reader effect, induced by `Std.Do`'s `WP (ReaderM σ)`. -/ |
| 167 | +def ReaderF.handler {σ : Type u} : LHandler (ReaderF σ) (.arg σ .pure) := |
| 168 | + LHandler.ofInterp (m := ReaderM σ) (fun _ op => FreeReader.readInterp op) |
| 169 | + |
| 170 | +instance ReaderF.instHasHandler {σ : Type u} : |
| 171 | + HasHandler (ReaderF σ) (.arg σ .pure) where |
| 172 | + handler := ReaderF.handler |
| 173 | + |
| 174 | +/-- WP of a `FreeReader` program matches WP of its `ReaderM` interpretation. -/ |
| 175 | +theorem ReaderF.wp_FreeReader_eq_wp_toReaderM {σ : Type u} (comp : FreeReader σ α) : |
| 176 | + wp comp = wp (FreeReader.toReaderM comp) := |
| 177 | + wpH_ofInterp_eq_wp_liftM (m := ReaderM σ) |
| 178 | + (fun _ op => FreeReader.readInterp op) comp |
| 179 | + |
| 180 | +/-- Hoare spec for `read` on `FreeReader`. -/ |
| 181 | +@[spec] |
| 182 | +theorem Spec.read_FreeReader {ρ : Type u} {Q : PostCond ρ (.arg ρ .pure)} : |
| 183 | + Triple (MonadReaderOf.read : FreeReader ρ ρ) (spred(fun r => Q.1 r r)) Q := by |
| 184 | + mvcgen |
| 185 | + |
| 186 | +end FreeM |
| 187 | + |
| 188 | +end Cslib |
0 commit comments