@@ -51,8 +51,6 @@ The `FreeM` monad is defined using an inductive type with constructors `.pure` a
5151We implement `Functor` and `Monad` instances, and prove the corresponding `LawfulFunctor`
5252and `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
5755The file `Free/Effects.lean` demonstrates practical applications by implementing State, Writer, and
5856Continuations monads using `FreeM` with appropriate effect signatures.
@@ -71,6 +69,9 @@ Free monad, state monad
7169
7270namespace 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
7677A `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''
9495namespace FreeM
9596variable {F : Type u → Type v} {ι : Type u} {α : Type w} {β : Type w'} {γ : Type w''}
9697
98+ section notations
99+
97100instance : 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 . -/
103108protected 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-
116113instance : 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. -/
123122def 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-
136126instance : 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. -/
143134def 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
175191instance : 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]
204220lemma 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
212228lemma 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]
217233lemma 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/--
227241A predicate stating that `interp : FreeM F α → m α` is an interpreter for the effect
@@ -237,23 +251,22 @@ Formally, `interp` satisfies the two equations:
237251 -/
238252structure 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
243257theorem 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
253266theorem 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/--
259272The universal property of the free monad `FreeM`.
0 commit comments