|
| 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 | +set_option mvcgen.warning false |
| 37 | + |
| 38 | +namespace Cslib |
| 39 | + |
| 40 | +open Std.Do |
| 41 | + |
| 42 | +namespace FreeM |
| 43 | + |
| 44 | +universe u v w |
| 45 | + |
| 46 | +variable {F G : Type u → Type v} {ps : PostShape.{u}} {α β : Type u} |
| 47 | + |
| 48 | +/-- A logical effect handler: an effect handler from `F` into the predicate-transformer monad |
| 49 | +`PredTrans ps`. Wrapped in a structure to isolate the implicit `ι`-binder from class resolution |
| 50 | +at use sites. -/ |
| 51 | +structure LHandler (F : Type u → Type v) (ps : PostShape.{u}) : Type (max (u + 1) v) where |
| 52 | + /-- The assignment from operations to predicate transformers. -/ |
| 53 | + run {ι : Type u} : F ι → PredTrans ps ι |
| 54 | + |
| 55 | +namespace LHandler |
| 56 | + |
| 57 | +/-- Sum of handlers; the counterpart of the paper's `H₁ ⊕ H₂`. -/ |
| 58 | +def sum (H₁ : LHandler F ps) (H₂ : LHandler G ps) : |
| 59 | + LHandler (fun α => F α ⊕ G α) ps where |
| 60 | + run op := match op with |
| 61 | + | .inl x => H₁.run x |
| 62 | + | .inr y => H₂.run y |
| 63 | + |
| 64 | +@[simp] theorem sum_run_inl (H₁ : LHandler F ps) (H₂ : LHandler G ps) |
| 65 | + {ι : Type u} (x : F ι) : |
| 66 | + (LHandler.sum H₁ H₂).run (Sum.inl x : F ι ⊕ G ι) = H₁.run x := rfl |
| 67 | + |
| 68 | +@[simp] theorem sum_run_inr (H₁ : LHandler F ps) (H₂ : LHandler G ps) |
| 69 | + {ι : Type u} (y : G ι) : |
| 70 | + (LHandler.sum H₁ H₂).run (Sum.inr y : F ι ⊕ G ι) = H₂.run y := rfl |
| 71 | + |
| 72 | +/-- Derive a logical handler from an effect handler into any `[WP m ps]` monad, by composing |
| 73 | +with `m`'s WP. -/ |
| 74 | +def ofInterp {m : Type u → Type w} [WP m ps] |
| 75 | + (interp : ∀ ι : Type u, F ι → m ι) : LHandler F ps where |
| 76 | + run {ι} op := wp (interp ι op) |
| 77 | + |
| 78 | +@[simp] theorem ofInterp_run {m : Type u → Type w} [WP m ps] |
| 79 | + (interp : ∀ ι : Type u, F ι → m ι) {ι : Type u} (op : F ι) : |
| 80 | + (LHandler.ofInterp interp).run op = wp (interp ι op) := rfl |
| 81 | + |
| 82 | +end LHandler |
| 83 | + |
| 84 | +/-- Weakest-precondition interpretation of a `FreeM F α` program against a logical handler `H`. |
| 85 | +Defined as `FreeM.liftM` instantiated at `PredTrans ps`, the unique monad morphism |
| 86 | +`FreeM F → PredTrans ps` extending `H` per the universal property of `FreeM`. -/ |
| 87 | +def wpH (H : LHandler F ps) (x : FreeM F α) : PredTrans ps α := |
| 88 | + x.liftM (fun {_} => H.run) |
| 89 | + |
| 90 | +@[simp] theorem wpH_pure (H : LHandler F ps) (a : α) : |
| 91 | + wpH H (pure a : FreeM F α) = Pure.pure a := rfl |
| 92 | + |
| 93 | +@[simp] theorem wpH_liftBind (H : LHandler F ps) {ι : Type u} |
| 94 | + (op : F ι) (k : ι → FreeM F α) : |
| 95 | + wpH H (lift op >>= k) = H.run op >>= fun x => wpH H (k x) := rfl |
| 96 | + |
| 97 | +@[simp] theorem wpH_lift (H : LHandler F ps) {ι : Type u} (op : F ι) : |
| 98 | + wpH H (lift op : FreeM F ι) = H.run op := |
| 99 | + liftM_lift _ op |
| 100 | + |
| 101 | +@[simp] theorem wpH_bind (H : LHandler F ps) (x : FreeM F α) (f : α → FreeM F β) : |
| 102 | + wpH H (x >>= f) = wpH H x >>= fun a => wpH H (f a) := |
| 103 | + liftM_bind _ x f |
| 104 | + |
| 105 | +/-- Adequacy theorem: WP via `FreeM` against an `ofInterp`-derived handler agrees with |
| 106 | +`Std.Do`'s WP of the `liftM` interpretation. Equivalently, two monad morphisms |
| 107 | +`FreeM F → PredTrans ps` extending the same handler are equal. -/ |
| 108 | +theorem wpH_ofInterp_eq_wp_liftM |
| 109 | + {m : Type u → Type w} [Monad m] [LawfulMonad m] [WPMonad m ps] |
| 110 | + (interp : ∀ ι : Type u, F ι → m ι) (x : FreeM F α) : |
| 111 | + wpH (LHandler.ofInterp interp) x = wp (x.liftM (fun {_} => interp _)) := by |
| 112 | + induction x with |
| 113 | + | pure a => simp [wpH, FreeM.liftM, WPMonad.wp_pure] |
| 114 | + | lift_bind op k ih => |
| 115 | + simp [WPMonad.wp_bind, ih] |
| 116 | + |
| 117 | +/-- Records a default logical handler for `F` at shape `ps`, enabling the global |
| 118 | +`WP (FreeM F) ps` instance and any `Triple`/`mvcgen` reasoning over `FreeM F`. -/ |
| 119 | +class HasHandler (F : Type u → Type v) (ps : outParam (PostShape.{u})) where |
| 120 | + /-- The default logical handler for `F`. -/ |
| 121 | + handler : LHandler F ps |
| 122 | + |
| 123 | +instance instWPFreeM [HasHandler F ps] : WP (FreeM F) ps where |
| 124 | + wp := wpH HasHandler.handler |
| 125 | + |
| 126 | +instance instWPMonadFreeM [HasHandler F ps] : WPMonad (FreeM F) ps where |
| 127 | + wp_pure _ := rfl |
| 128 | + wp_bind x f := wpH_bind _ x f |
| 129 | + |
| 130 | +/-- Logical handler for the state effect, induced by `Std.Do`'s `WP (StateM σ)`. -/ |
| 131 | +def StateF.handler {σ : Type u} : LHandler (StateF σ) (.arg σ .pure) := |
| 132 | + LHandler.ofInterp (m := StateM σ) (fun _ op => FreeState.stateInterp op) |
| 133 | + |
| 134 | +instance StateF.instHasHandler {σ : Type u} : |
| 135 | + HasHandler (StateF σ) (.arg σ .pure) where |
| 136 | + handler := StateF.handler |
| 137 | + |
| 138 | +/-- WP of a `FreeState` program matches WP of its `StateM` interpretation. -/ |
| 139 | +theorem StateF.wp_FreeState_eq_wp_toStateM {σ : Type u} (comp : FreeState σ α) : |
| 140 | + wp comp = wp (FreeState.toStateM comp) := |
| 141 | + wpH_ofInterp_eq_wp_liftM (m := StateM σ) |
| 142 | + (fun _ op => FreeState.stateInterp op) comp |
| 143 | + |
| 144 | +/-- Hoare spec for `get` on `FreeState`. -/ |
| 145 | +@[spec] |
| 146 | +theorem Spec.get_FreeState {σ : Type u} {Q : PostCond σ (.arg σ .pure)} : |
| 147 | + Triple (MonadStateOf.get : FreeState σ σ) (spred(fun s => Q.1 s s)) Q := by |
| 148 | + mvcgen |
| 149 | + |
| 150 | +/-- Hoare spec for `set` on `FreeState`. -/ |
| 151 | +@[spec] |
| 152 | +theorem Spec.set_FreeState {σ : Type u} (s : σ) {Q : PostCond PUnit (.arg σ .pure)} : |
| 153 | + Triple (MonadStateOf.set s : FreeState σ PUnit) (spred(fun _ => Q.1 ⟨⟩ s)) Q := by |
| 154 | + mvcgen |
| 155 | + |
| 156 | +/-- Logical handler for the reader effect, induced by `Std.Do`'s `WP (ReaderM σ)`. -/ |
| 157 | +def ReaderF.handler {σ : Type u} : LHandler (ReaderF σ) (.arg σ .pure) := |
| 158 | + LHandler.ofInterp (m := ReaderM σ) (fun _ op => FreeReader.readInterp op) |
| 159 | + |
| 160 | +instance ReaderF.instHasHandler {σ : Type u} : |
| 161 | + HasHandler (ReaderF σ) (.arg σ .pure) where |
| 162 | + handler := ReaderF.handler |
| 163 | + |
| 164 | +/-- WP of a `FreeReader` program matches WP of its `ReaderM` interpretation. -/ |
| 165 | +theorem ReaderF.wp_FreeReader_eq_wp_toReaderM {σ : Type u} (comp : FreeReader σ α) : |
| 166 | + wp comp = wp (FreeReader.toReaderM comp) := |
| 167 | + wpH_ofInterp_eq_wp_liftM (m := ReaderM σ) |
| 168 | + (fun _ op => FreeReader.readInterp op) comp |
| 169 | + |
| 170 | +/-- Hoare spec for `read` on `FreeReader`. -/ |
| 171 | +@[spec] |
| 172 | +theorem Spec.read_FreeReader {ρ : Type u} {Q : PostCond ρ (.arg ρ .pure)} : |
| 173 | + Triple (MonadReaderOf.read : FreeReader ρ ρ) (spred(fun r => Q.1 r r)) Q := by |
| 174 | + mvcgen |
| 175 | + |
| 176 | +end FreeM |
| 177 | + |
| 178 | +end Cslib |
0 commit comments