Skip to content

Commit 1d58236

Browse files
committed
refactor(FreeWP): inline handler wrappers, rename HasHandler to FreeM.WP
Address review feedback: - inline `wpH`, the `LHandler` abbrev/namespace, and `LHandler.ofInterp`; handlers are now plain `F ι → PredTrans ps ι` functions - rename the `HasHandler` class to `FreeM.WP` - restate the adequacy lemma directly on `liftM` as `liftM_wp_eq_wp_liftM`
1 parent c415fc2 commit 1d58236

3 files changed

Lines changed: 40 additions & 90 deletions

File tree

Cslib/Foundations/Control/Monad/Free/Effects.lean

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,17 +163,18 @@ lemma run'_bind (x : FreeState σ α) (f : α → FreeState σ β) (s₀ : σ) :
163163
end FreeState
164164

165165
/-- Logical handler for the state effect, induced by `Std.Do`'s `WP (StateM σ)`. -/
166-
def StateF.handler {σ : Type u} : LHandler (StateF σ) (.arg σ .pure) :=
167-
LHandler.ofInterp (m := StateM σ) (fun _ op => FreeState.stateInterp op)
166+
def StateF.handler {σ : Type u} :
167+
{ι : Type u} → StateF σ ι → PredTrans (.arg σ .pure) ι :=
168+
fun {ι} op => wp (FreeState.stateInterp (σ := σ) (α := ι) op)
168169

169-
instance StateF.instHasHandler {σ : Type u} :
170-
HasHandler (StateF σ) (.arg σ .pure) where
170+
instance StateF.instWP {σ : Type u} :
171+
FreeM.WP (StateF σ) (.arg σ .pure) where
171172
handler := StateF.handler
172173

173174
/-- WP of a `FreeState` program matches WP of its `StateM` interpretation. -/
174175
theorem StateF.wp_FreeState_eq_wp_toStateM {σ α : Type u} (comp : FreeState σ α) :
175176
wp comp = wp (FreeState.toStateM comp) :=
176-
wpH_ofInterp_eq_wp_liftM (m := StateM σ)
177+
liftM_wp_eq_wp_liftM (m := StateM σ)
177178
(fun _ op => FreeState.stateInterp op) comp
178179

179180
/-- Hoare spec for `get` on `FreeState`. -/
@@ -485,17 +486,18 @@ instance instMonadWithReaderOf : MonadWithReaderOf σ (FreeReader σ) where
485486
end FreeReader
486487

487488
/-- Logical handler for the reader effect, induced by `Std.Do`'s `WP (ReaderM σ)`. -/
488-
def ReaderF.handler {σ : Type u} : LHandler (ReaderF σ) (.arg σ .pure) :=
489-
LHandler.ofInterp (m := ReaderM σ) (fun _ op => FreeReader.readInterp op)
489+
def ReaderF.handler {σ : Type u} :
490+
{ι : Type u} → ReaderF σ ι → PredTrans (.arg σ .pure) ι :=
491+
fun {ι} op => wp (FreeReader.readInterp (σ := σ) (α := ι) op)
490492

491-
instance ReaderF.instHasHandler {σ : Type u} :
492-
HasHandler (ReaderF σ) (.arg σ .pure) where
493+
instance ReaderF.instWP {σ : Type u} :
494+
FreeM.WP (ReaderF σ) (.arg σ .pure) where
493495
handler := ReaderF.handler
494496

495497
/-- WP of a `FreeReader` program matches WP of its `ReaderM` interpretation. -/
496498
theorem ReaderF.wp_FreeReader_eq_wp_toReaderM {σ α : Type u} (comp : FreeReader σ α) :
497499
wp comp = wp (FreeReader.toReaderM comp) :=
498-
wpH_ofInterp_eq_wp_liftM (m := ReaderM σ)
500+
liftM_wp_eq_wp_liftM (m := ReaderM σ)
499501
(fun _ op => FreeReader.readInterp op) comp
500502

501503
/-- Hoare spec for `read` on `FreeReader`. -/

Cslib/Foundations/Control/Monad/Free/WP.lean

Lines changed: 16 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ public import Std.Do.Triple
1717
1818
Weakest-precondition interpretation of `FreeM F` programs through `Std.Do`'s
1919
predicate-transformer monad `PredTrans ps`. The universal property of `FreeM` lifts any
20-
effect handler `F ι → PredTrans ps ι` to a unique monad morphism `wpH H = liftM H`,
20+
effect handler `F ι → PredTrans ps ι` to a unique monad morphism `liftM H`,
2121
so weakest preconditions are compositional in `FreeM`'s monadic structure. A
22-
`[HasHandler F ps]` instance plugs `FreeM F` into `Std.Do`'s `WP`/`WPMonad`/`Triple`
22+
`[FreeM.WP F ps]` instance plugs `FreeM F` into `Std.Do`'s `WP`/`WPMonad`/`Triple`
2323
infrastructure.
2424
25-
The WP's structural rules (`wpH_pure`, `wpH_bind`, …) are immediate from `liftM` being a monad
26-
morphism; the adequacy theorem `wpH_ofInterp_eq_wp_liftM` — that WP-via-handler agrees with
25+
The WP's structural rules are immediate from `liftM` being a monad morphism; the
26+
adequacy theorem `liftM_wp_eq_wp_liftM` — that WP-via-handler agrees with
2727
`Std.Do`'s WP of the `liftM` interpretation — is the same statement of uniqueness.
2828
2929
The design follows [Vistrup, Sammler, Jung. *Program Logics à la Carte.* POPL 2025], adapted
@@ -42,85 +42,33 @@ namespace FreeM
4242

4343
universe u v w
4444

45-
variable {F G : Type u → Type v} {ps : PostShape.{u}} {α β : Type u}
45+
variable {F : Type u → Type v} {ps : PostShape.{u}} {α β : Type u}
4646

47-
/-- A logical handler: an effect handler from `F` into the predicate-transformer monad
48-
`PredTrans ps`. -/
49-
abbrev LHandler (F : Type u → Type v) (ps : PostShape.{u}) : Type (max (u + 1) v) :=
50-
∀ {ι : Type u}, F ι → PredTrans ps ι
51-
52-
namespace LHandler
53-
54-
/-- Sum of handlers; the counterpart of the paper's `H₁ ⊕ H₂`. -/
55-
def sum (H₁ : LHandler F ps) (H₂ : LHandler G ps) :
56-
LHandler (fun α => F α ⊕ G α) ps :=
57-
fun op => Sum.elim H₁ H₂ op
58-
59-
@[simp] theorem sum_inl (H₁ : LHandler F ps) (H₂ : LHandler G ps)
60-
{ι : Type u} (x : F ι) :
61-
LHandler.sum H₁ H₂ (Sum.inl x : F ι ⊕ G ι) = H₁ x := rfl
62-
63-
@[simp] theorem sum_inr (H₁ : LHandler F ps) (H₂ : LHandler G ps)
64-
{ι : Type u} (y : G ι) :
65-
LHandler.sum H₁ H₂ (Sum.inr y : F ι ⊕ G ι) = H₂ y := rfl
66-
67-
/-- Derive a logical handler from an effect handler into any `[WP m ps]` monad, by composing
68-
with `m`'s WP. -/
69-
def ofInterp {m : Type u → Type w} [WP m ps]
70-
(interp : ∀ ι : Type u, F ι → m ι) : LHandler F ps :=
71-
fun {ι} op => wp (interp ι op)
72-
73-
@[simp] theorem ofInterp_apply {m : Type u → Type w} [WP m ps]
74-
(interp : ∀ ι : Type u, F ι → m ι) {ι : Type u} (op : F ι) :
75-
LHandler.ofInterp interp op = wp (interp ι op) := rfl
76-
77-
end LHandler
78-
79-
/-- Weakest-precondition interpretation of a `FreeM F α` program against a logical handler `H`.
80-
Defined as `FreeM.liftM` instantiated at `PredTrans ps`, the unique monad morphism
81-
`FreeM F → PredTrans ps` extending `H` per the universal property of `FreeM`. -/
82-
def wpH (H : LHandler F ps) (x : FreeM F α) : PredTrans ps α :=
83-
x.liftM H
84-
85-
@[simp] theorem wpH_pure (H : LHandler F ps) (a : α) :
86-
wpH H (pure a : FreeM F α) = Pure.pure a := rfl
87-
88-
@[simp] theorem wpH_liftBind (H : LHandler F ps) {ι : Type u}
89-
(op : F ι) (k : ι → FreeM F α) :
90-
wpH H (lift op >>= k) = H op >>= fun x => wpH H (k x) := rfl
91-
92-
@[simp] theorem wpH_lift (H : LHandler F ps) {ι : Type u} (op : F ι) :
93-
wpH H (lift op : FreeM F ι) = H op :=
94-
liftM_lift _ op
95-
96-
@[simp] theorem wpH_bind (H : LHandler F ps) (x : FreeM F α) (f : α → FreeM F β) :
97-
wpH H (x >>= f) = wpH H x >>= fun a => wpH H (f a) :=
98-
liftM_bind _ x f
99-
100-
/-- Adequacy theorem: WP via `FreeM` against an `ofInterp`-derived handler agrees with
47+
/-- Adequacy theorem: WP via `FreeM` against a WP-derived handler agrees with
10148
`Std.Do`'s WP of the `liftM` interpretation. Equivalently, two monad morphisms
10249
`FreeM F → PredTrans ps` extending the same handler are equal. -/
103-
theorem wpH_ofInterp_eq_wp_liftM
104-
{m : Type u → Type w} [Monad m] [LawfulMonad m] [WPMonad m ps]
50+
theorem liftM_wp_eq_wp_liftM
51+
{m : Type u → Type w} [Monad m] [WPMonad m ps]
10552
(interp : ∀ ι : Type u, F ι → m ι) (x : FreeM F α) :
106-
wpH (LHandler.ofInterp interp) x = wp (x.liftM (fun {_} => interp _)) := by
53+
x.liftM (fun {ι} op => wp (interp ι op)) =
54+
wp (x.liftM (fun {_} => interp _)) := by
10755
induction x with
108-
| pure a => simp [wpH, FreeM.liftM, WPMonad.wp_pure]
56+
| pure a => simp [WPMonad.wp_pure]
10957
| lift_bind op k ih =>
11058
simp [WPMonad.wp_bind, ih]
11159

11260
/-- Records a default logical handler for `F` at shape `ps`, enabling the global
11361
`WP (FreeM F) ps` instance and any `Triple`/`mvcgen` reasoning over `FreeM F`. -/
114-
class HasHandler (F : Type u → Type v) (ps : outParam (PostShape.{u})) where
62+
class WP (F : Type u → Type v) (ps : outParam (PostShape.{u})) where
11563
/-- The default logical handler for `F`. -/
11664
handler {ι : Type u} : F ι → PredTrans ps ι
11765

118-
instance instWPFreeM [HasHandler F ps] : WP (FreeM F) ps where
119-
wp := wpH HasHandler.handler
66+
instance instStdDoWP [WP F ps] : Std.Do.WP (FreeM F) ps where
67+
wp x := x.liftM WP.handler
12068

121-
instance instWPMonadFreeM [HasHandler F ps] : WPMonad (FreeM F) ps where
69+
instance instWPMonadFreeM [WP F ps] : WPMonad (FreeM F) ps where
12270
wp_pure _ := rfl
123-
wp_bind x f := wpH_bind _ x f
71+
wp_bind x f := liftM_bind _ x f
12472

12573
end FreeM
12674

CslibTests/FreeMonadWP.lean

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ namespace CslibTests.FreeMonadWP
1919

2020
open Cslib Cslib.FreeM Std.Do
2121

22-
example : WP (FreeState Nat) (.arg Nat .pure) := inferInstance
22+
example : Std.Do.WP (FreeState Nat) (.arg Nat .pure) := inferInstance
2323
example : WPMonad (FreeState Nat) (.arg Nat .pure) := inferInstance
24-
example : WP (FreeReader Nat) (.arg Nat .pure) := inferInstance
25-
example : HasHandler (StateF Nat) (.arg Nat .pure) := inferInstance
24+
example : Std.Do.WP (FreeReader Nat) (.arg Nat .pure) := inferInstance
25+
example : FreeM.WP (StateF Nat) (.arg Nat .pure) := inferInstance
2626

2727
/-- Increment the natural-number state by 1. -/
2828
def incr : FreeState Nat Unit := do
@@ -61,10 +61,10 @@ def interp : ∀ ι : Type, CounterF ι → StateM Nat ι
6161

6262
/-- Logical handler for `CounterF` induced by `interp` and `Std.Do`'s `WP (StateM Nat)`
6363
instance. -/
64-
def handler : LHandler CounterF (.arg Nat .pure) :=
65-
LHandler.ofInterp CounterF.interp
64+
def handler : {ι : Type} → CounterF ι → PredTrans (.arg Nat .pure) ι :=
65+
fun {ι} op => wp (CounterF.interp ι op)
6666

67-
instance : HasHandler CounterF (.arg Nat .pure) where
67+
instance : FreeM.WP CounterF (.arg Nat .pure) where
6868
handler := CounterF.handler
6969

7070
/-- Interpret counter programs as `StateM Nat` programs. -/
@@ -74,7 +74,7 @@ abbrev toStateM {α : Type} (comp : FreeCounter α) : StateM Nat α :=
7474
/-- Adequacy theorem specialized to `CounterF`. -/
7575
theorem wp_FreeCounter_eq_wp_toStateM {α : Type} (comp : FreeCounter α) :
7676
wp comp = wp (CounterF.toStateM comp) :=
77-
wpH_ofInterp_eq_wp_liftM (m := StateM Nat) CounterF.interp comp
77+
liftM_wp_eq_wp_liftM (m := StateM Nat) CounterF.interp comp
7878

7979
end CounterF
8080

@@ -107,7 +107,7 @@ inductive FailF : Type → Type where
107107

108108
/-- Logical handler for `FailF`: `fail` has precondition `⌜False⌝`, so it is only provable in
109109
unreachable branches. -/
110-
def FailF.handler {ps : PostShape} : LHandler FailF ps :=
110+
def FailF.handler {ps : PostShape} : {ι : Type} → FailF ι → PredTrans ps ι :=
111111
fun op => match op with
112112
| .fail => PredTrans.const spred(⌜False⌝)
113113

@@ -116,8 +116,8 @@ abbrev StateFail := fun α => StateF Nat α ⊕ FailF α
116116

117117
/-- Handler for the combined signature: the sum of the component handlers — the paper's
118118
`H₁ ⊕ H₂` composition. -/
119-
instance : HasHandler StateFail (.arg Nat .pure) where
120-
handler := StateF.handler.sum FailF.handler
119+
instance : FreeM.WP StateFail (.arg Nat .pure) where
120+
handler := fun op => Sum.elim StateF.handler FailF.handler op
121121

122122
/-- Smart constructor for state-read in the combined signature. -/
123123
abbrev sfGet : FreeM StateFail Nat := lift (Sum.inl StateF.get)
@@ -186,7 +186,7 @@ inductive DemonicF : Type → Type 1 where
186186
/-- Logical handler for `DemonicF`: the predicate transformer for `choice α` is universal
187187
quantification over `α`. Conjunctivity of `∀` (i.e. `∀ a, P a ∧ Q a ⊣⊢ (∀ a, P a) ∧ (∀ a, Q a)`)
188188
is what makes this admissible in `PredTrans`. -/
189-
def DemonicF.handler {ps : PostShape} : LHandler DemonicF ps :=
189+
def DemonicF.handler {ps : PostShape} : {ι : Type} → DemonicF ι → PredTrans ps ι :=
190190
fun op => match op with
191191
| .choice _ =>
192192
{ trans := fun Q => SPred.forall (fun a => Q.1 a)
@@ -207,7 +207,7 @@ def DemonicF.handler {ps : PostShape} : LHandler DemonicF ps :=
207207
· exact SPred.and_elim_l.trans (SPred.forall_elim a)
208208
· exact SPred.and_elim_r.trans (SPred.forall_elim a) }
209209

210-
instance : HasHandler DemonicF .pure where
210+
instance : FreeM.WP DemonicF .pure where
211211
handler := DemonicF.handler
212212

213213
/-- Smart constructor for demonic choice over `α`. -/

0 commit comments

Comments
 (0)