Skip to content

Commit 666ea00

Browse files
committed
refactor: change the simpNF for FreeM
1 parent 83f543e commit 666ea00

3 files changed

Lines changed: 148 additions & 99 deletions

File tree

Cslib/Foundations/Control/Monad/Free.lean

Lines changed: 65 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ The `FreeM` monad is defined using an inductive type with constructors `.pure` a
5151
We implement `Functor` and `Monad` instances, and prove the corresponding `LawfulFunctor`
5252
and `LawfulMonad` instances.
5353
54-
For now we choose to make the constructors the simp-normal form, as opposed to the standard
55-
monad notation.
5654
5755
The file `Free/Effects.lean` demonstrates practical applications by implementing State, Writer, and
5856
Continuations monads using `FreeM` with appropriate effect signatures.
@@ -71,6 +69,9 @@ Free monad, state monad
7169

7270
namespace Cslib
7371

72+
-- Disable generation of unneeded lemmas which the simpNF linter would complain about.
73+
set_option genInjectivity false in
74+
set_option genSizeOfSpec false in
7475
/-- The Free monad over a type constructor `F`.
7576
7677
A `FreeM F a` is a tree of operations from the type constructor `F`, with leaves of type `a`.
@@ -94,83 +95,98 @@ universe u v w w' w''
9495
namespace FreeM
9596
variable {F : Type u → Type v} {ι : Type u} {α : Type w} {β : Type w'} {γ : Type w''}
9697

98+
section notations
99+
97100
instance : Pure (FreeM F) where pure := .pure
98101

99102
@[simp]
100-
theorem pure_eq_pure : (pure : α → FreeM F α) = FreeM.pure := rfl
103+
theorem pure_eq_pure : FreeM.pure = (pure : α → FreeM F α) := rfl
104+
105+
/-- Bind operation for the `FreeM` monad.
101106
102-
/-- Bind operation for the `FreeM` monad. -/
107+
The builtin `>>=` notation should be preferred when `α` and `β` are in the same universe. -/
103108
protected def bind (x : FreeM F α) (f : α → FreeM F β) : FreeM F β :=
104109
match x with
105110
| .pure a => f a
106111
| .liftBind op cont => .liftBind op fun z => FreeM.bind (cont z) f
107112

108-
protected theorem bind_assoc (x : FreeM F α) (f : α → FreeM F β) (g : β → FreeM F γ) :
109-
(x.bind f).bind g = x.bind (fun x => (f x).bind g) := by
110-
induction x with
111-
| pure a => rfl
112-
| liftBind op cont ih =>
113-
simp [FreeM.bind] at *
114-
simp [ih]
115-
116113
instance : Bind (FreeM F) where bind := .bind
117114

115+
/-- Note that this lemma does not always apply, as it is universe-constrained by `Bind.bind`. -/
118116
@[simp]
119-
theorem bind_eq_bind {α β : Type w} : Bind.bind = (FreeM.bind : FreeM F α → _ → FreeM F β) := rfl
117+
theorem bind_eq_bind {α β : Type w} : (FreeM.bind : FreeM F α → _ → FreeM F β) = Bind.bind := rfl
120118

121-
/-- Map a function over a `FreeM` monad. -/
122-
@[simp]
119+
/-- Map a function over a `FreeM` monad.
120+
121+
The builtin `<$>` notation should be preferred when `α` and `β` are in the same universe. -/
123122
def map (f : α → β) : FreeM F α → FreeM F β
124123
| .pure a => .pure (f a)
125124
| .liftBind op cont => .liftBind op fun z => FreeM.map f (cont z)
126125

127-
@[simp]
128-
theorem id_map : ∀ x : FreeM F α, map id x = x
129-
| .pure a => rfl
130-
| .liftBind op cont => by simp_all [map, id_map]
131-
132-
theorem comp_map (h : β → γ) (g : α → β) : ∀ x : FreeM F α, map (h ∘ g) x = map h (map g x)
133-
| .pure a => rfl
134-
| .liftBind op cont => by simp_all [map, comp_map]
135-
136126
instance : Functor (FreeM F) where
137127
map := .map
138128

129+
/-- Note that this lemma does not always apply, as it is universe-constrained by `Functor.map`. -/
139130
@[simp]
140-
theorem map_eq_map {α β : Type w} : Functor.map = FreeM.map (F := F) (α := α) (β := β) := rfl
131+
theorem map_eq_map {α β : Type w} : FreeM.map (F := F) (α := α) (β := β) = Functor.map := rfl
141132

142133
/-- Lift an operation from the effect signature `F` into the `FreeM F` monad. -/
143134
def lift (op : F ι) : FreeM F ι :=
144135
.liftBind op .pure
145136

146-
/-- Rewrite `lift` to the constructor form so that simplification stays in constructor normal
147-
form. -/
148137
@[simp]
149-
lemma lift_def (op : F ι) :
150-
(lift op : FreeM F ι) = liftBind op .pure := rfl
138+
lemma liftBind_eq (op : F ι) :
139+
liftBind op cont = (lift op : FreeM F ι).bind cont :=
140+
rfl
151141

152-
@[simp]
153-
lemma map_lift (f : ι → α) (op : F ι) :
154-
map f (lift op : FreeM F ι) = liftBind op (fun z => (.pure (f z) : FreeM F α)) := rfl
142+
set_option linter.unusedVariables false in
143+
/-- An override for the default induction principle that is in simp-normal form. -/
144+
@[induction_eliminator]
145+
protected theorem induction {motive : FreeM F α → Prop}
146+
(pure : ∀ a, motive (pure a))
147+
(lift_bind : ∀ {ι} (op : F ι) (cont : ι → FreeM F α) (ih : ∀ i, motive (cont i)),
148+
motive ((lift op).bind cont)) : ∀ x, motive x
149+
| .pure a => pure a
150+
| liftBind _ _ => lift_bind _ _ fun _ => FreeM.induction pure lift_bind _
151+
152+
end notations
153+
154+
protected theorem bind_assoc (x : FreeM F α) (f : α → FreeM F β) (g : β → FreeM F γ) :
155+
(x.bind f).bind g = x.bind (fun x => (f x).bind g) := by
156+
induction x with
157+
| pure a => rfl
158+
| lift_bind op cont ih => simp [← liftBind_eq, FreeM.bind, ih] at *
155159

156160
/-- `.pure a` followed by `bind` collapses immediately. -/
157161
@[simp]
158-
lemma pure_bind (a : α) (f : α → FreeM F β) : (.pure a : FreeM F α).bind f = f a := rfl
162+
lemma pure_bind (a : α) (f : α → FreeM F β) : (pure a : FreeM F α).bind f = f a := rfl
159163

160164
@[simp]
161-
lemma bind_pure : ∀ x : FreeM F α, x.bind (.pure) = x
165+
lemma bind_pure : ∀ x : FreeM F α, x.bind pure = x
162166
| .pure a => rfl
163-
| liftBind op k => by simp [FreeM.bind, bind_pure]
167+
| liftBind op k => by simp [FreeM.bind, bind_pure, -bind_eq_bind]
164168

165169
@[simp]
166-
lemma bind_pure_comp (f : α → β) : ∀ x : FreeM F α, x.bind (.pure ∘ f) = map f x
170+
lemma bind_pure_comp (f : α → β) : ∀ x : FreeM F α, x.bind (pure ∘ f) = map f x
167171
| .pure a => rfl
168172
| liftBind op k => by simp only [FreeM.bind, map, bind_pure_comp]
169173

170-
/-- Collapse a `.bind` that follows a `liftBind` into a single `liftBind` -/
171174
@[simp]
172-
lemma liftBind_bind (op : F ι) (cont : ι → FreeM F α) (f : α → FreeM F β) :
173-
(liftBind op cont).bind f = liftBind op fun x => (cont x).bind f := rfl
175+
theorem map_pure (f : α → β) (x : α) : map f (pure x : FreeM F α) = pure (f x) := rfl
176+
177+
@[simp]
178+
theorem map_bind (f : β → γ) (x : FreeM F α) (c : α → FreeM F β) :
179+
map f (x.bind c) = x.bind fun a => (c a).map f := by
180+
simp_rw [← bind_pure_comp, FreeM.bind_assoc]
181+
182+
@[simp]
183+
theorem id_map : ∀ x : FreeM F α, map id x = x
184+
| .pure a => rfl
185+
| .liftBind op cont => by simp_all [map, id_map]
186+
187+
theorem comp_map (h : β → γ) (g : α → β) : ∀ x : FreeM F α, map (h ∘ g) x = map h (map g x)
188+
| .pure a => rfl
189+
| .liftBind op cont => by simp_all [map, comp_map]
174190

175191
instance : LawfulFunctor (FreeM F) where
176192
map_const := rfl
@@ -202,26 +218,24 @@ protected def liftM (interp : {ι : Type u} → F ι → m ι) : FreeM F α →
202218

203219
@[simp]
204220
lemma liftM_pure (interp : {ι : Type u} → F ι → m ι) (a : α) :
205-
(.pure a : FreeM F α).liftM interp = pure a := rfl
221+
(pure a : FreeM F α).liftM interp = pure a := rfl
206222

207223
@[simp]
208-
lemma liftM_liftBind (interp : {ι : Type u} → F ι → m ι) (op : F β) (cont : β → FreeM F α) :
209-
(liftBind op cont).liftM interp = (do let b ← interp op; (cont b).liftM interp) := by
224+
lemma liftM_lift_bind (interp : {ι : Type u} → F ι → m ι) (op : F β) (cont : β → FreeM F α) :
225+
((lift op) >>= cont).liftM interp = (do let b ← interp op; (cont b).liftM interp) := by
210226
rfl
211227

212228
lemma liftM_lift [LawfulMonad m] (interp : {ι : Type u} → F ι → m ι) (op : F β) :
213229
(lift op).liftM interp = interp op := by
214-
simp_rw [lift_def, liftM_liftBind, liftM_pure, _root_.bind_pure]
230+
simp_rw [lift, FreeM.liftM, _root_.bind_pure]
215231

216232
@[simp]
217233
lemma liftM_bind [LawfulMonad m]
218234
(interp : {ι : Type u} → F ι → m ι) (x : FreeM F α) (f : α → FreeM F β) :
219235
(x.bind f : FreeM F β).liftM interp = (do let a ← x.liftM interp; (f a).liftM interp) := by
220236
induction x generalizing f with
221237
| pure a => simp only [pure_bind, liftM_pure, LawfulMonad.pure_bind]
222-
| liftBind op cont ih =>
223-
rw [FreeM.bind, liftM_liftBind, liftM_liftBind, bind_assoc]
224-
simp_rw [ih]
238+
| lift_bind op cont ih => simp [← ih]
225239

226240
/--
227241
A predicate stating that `interp : FreeM F α → m α` is an interpreter for the effect
@@ -237,23 +251,22 @@ Formally, `interp` satisfies the two equations:
237251
-/
238252
structure Interprets (handler : {ι : Type u} → F ι → m ι) (interp : FreeM F α → m α) : Prop where
239253
apply_pure (a : α) : interp (.pure a) = pure a
240-
apply_liftBind {ι : Type u} (op : F ι) (cont : ι → FreeM F α) :
241-
interp (liftBind op cont) = handler op >>= fun x => interp (cont x)
254+
apply_lift_bind {ι : Type u} (op : F ι) (cont : ι → FreeM F α) :
255+
interp (lift op >>= cont) = handler op >>= fun x => interp (cont x)
242256

243257
theorem Interprets.eq {handler : {ι : Type u} → F ι → m ι} {interp : FreeM F α → m α}
244258
(h : Interprets handler interp) :
245259
interp = (·.liftM @handler) := by
246260
ext x
247261
induction x with
248262
| pure a => exact h.apply_pure a
249-
| liftBind op cont ih =>
250-
rw [liftM_liftBind, h.apply_liftBind]
251-
simp [ih]
263+
| lift_bind op cont ih =>
264+
simp [h.apply_lift_bind, ih]
252265

253266
theorem Interprets.liftM (handler : {ι : Type u} → F ι → m ι) :
254267
Interprets handler (·.liftM handler : FreeM F α → _) where
255268
apply_pure _ := rfl
256-
apply_liftBind _ _ := rfl
269+
apply_lift_bind _ _ := rfl
257270

258271
/--
259272
The universal property of the free monad `FreeM`.

0 commit comments

Comments
 (0)