Skip to content

Commit 26c0474

Browse files
authored
feat: adding free reader monad API and minor edits to free writer and free cont (#247)
This PR adds an API for the `Reader` monad represented as a free monad, to the list of existing free effects. Also made some minor changes to the `Writer` and `Cont` APIs in this same file
1 parent df95c8f commit 26c0474

1 file changed

Lines changed: 84 additions & 16 deletions

File tree

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

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ Authors: Tanner Duve
77
module
88

99
public import Cslib.Foundations.Control.Monad.Free
10+
public import Mathlib.Algebra.Group.Hom.Defs
1011
public import Mathlib.Control.Monad.Cont
12+
public import Mathlib.Control.Monad.Writer
1113

1214
@[expose] public section
1315

@@ -18,14 +20,16 @@ This file defines several canonical instances on the free monad.
1820
1921
## Main definitions
2022
21-
- `FreeState`, `FreeWriter`, `FreeCont`: Specific effect monads
23+
- `FreeState`, `FreeWriter`, `FreeCont`, `FreeReader`: Specific effect monads
2224
2325
## Implementation
2426
2527
To execute or interpret these computations, we provide two approaches:
26-
1. **Hand-written interpreters** (`FreeState.run`, `FreeWriter.run`, `FreeCont.run`) that directly
28+
1. **Hand-written interpreters** (`FreeState.run`, `FreeWriter.run`, `FreeCont.run`,
29+
`FreeReader.run`) that directly
2730
pattern-match on the tree structure
28-
2. **Canonical interpreters** (`FreeState.toStateM`, `FreeWriter.toWriterT`, `FreeCont.toContT`)
31+
2. **Canonical interpreters** (`FreeState.toStateM`, `FreeWriter.toWriterT`, `FreeCont.toContT`,
32+
`FreeReader.toReaderM`)
2933
derived from the universal property via `liftM`
3034
3135
We prove that these approaches are equivalent, demonstrating that the implementation aligns with
@@ -58,9 +62,6 @@ abbrev FreeState (σ : Type u) := FreeM (StateF σ)
5862
namespace FreeState
5963
variable {σ : Type u} {α : Type v}
6064

61-
instance : Monad (FreeState σ) := inferInstance
62-
instance : LawfulMonad (FreeState σ) := inferInstance
63-
6465
instance : MonadStateOf σ (FreeState σ) where
6566
get := .lift .get
6667
set newState := .liftBind (.set newState) (fun _ => .pure PUnit.unit)
@@ -75,8 +76,6 @@ lemma get_def : (get : FreeState σ σ) = .lift .get := rfl
7576
@[simp]
7677
lemma set_def (s : σ) : (set s : FreeState σ PUnit) = .lift (.set s) := rfl
7778

78-
instance : MonadState σ (FreeState σ) := inferInstance
79-
8079
/-- Interpret `StateF` operations into `StateM`. -/
8180
def stateInterp {α : Type u} : StateF σ α → StateM σ α
8281
| .get => MonadStateOf.get
@@ -164,10 +163,7 @@ abbrev FreeWriter (ω : Type u) := FreeM (WriterF ω)
164163
namespace FreeWriter
165164

166165
open WriterF
167-
variable {ω : Type u} {α : Type v}
168-
169-
instance : Monad (FreeWriter ω) := inferInstance
170-
instance : LawfulMonad (FreeWriter ω) := inferInstance
166+
variable {ω : Type u} {α : Type u}
171167

172168
/-- Interpret `WriterF` operations into `WriterT`. -/
173169
def writerInterp {α : Type u} : WriterF ω α → WriterT ω Id α
@@ -284,12 +280,9 @@ abbrev FreeCont (r : Type u) := FreeM (ContF r)
284280
namespace FreeCont
285281
variable {r : Type u} {α : Type v} {β : Type w}
286282

287-
instance : Monad (FreeCont r) := inferInstance
288-
instance : LawfulMonad (FreeCont r) := inferInstance
289-
290283
/-- Interpret `ContF r` operations into `ContT r Id`. -/
291284
def contInterp : ContF r α → ContT r Id α
292-
| .callCC g, k => pure (g fun a => (k a).run)
285+
| .callCC g => g
293286

294287
/-- Convert a `FreeCont` computation into a `ContT` computation. This is the canonical
295288
interpreter derived from `liftM`. -/
@@ -353,6 +346,81 @@ lemma run_callCC (f : MonadCont.Label α (FreeCont r) β → FreeCont r α) (k :
353346

354347
end FreeCont
355348

349+
/-- Type constructor for reader operations. -/
350+
inductive ReaderF (σ : Type u) : Type u → Type u where
351+
| read : ReaderF σ σ
352+
353+
/-- Reader monad via the `FreeM` monad -/
354+
abbrev FreeReader (σ) := FreeM (ReaderF σ)
355+
356+
namespace FreeReader
357+
358+
variable {σ : Type u} {α : Type u}
359+
360+
instance : MonadReaderOf σ (FreeReader σ) where
361+
read := .lift .read
362+
363+
@[simp]
364+
lemma read_def : (read : FreeReader σ σ) = .lift .read := rfl
365+
366+
instance : MonadReader σ (FreeReader σ) := inferInstance
367+
368+
/-- Interpret `ReaderF` operations into `ReaderM`. -/
369+
def readInterp {α : Type u} : ReaderF σ α → ReaderM σ α
370+
| .read => MonadReaderOf.read
371+
372+
/-- Convert a `FreeReader` computation into a `ReaderM` computation. This is the canonical
373+
interpreter derived from `liftM`. -/
374+
def toReaderM {α : Type u} (comp : FreeReader σ α) : ReaderM σ α :=
375+
comp.liftM readInterp
376+
377+
/-- `toReaderM` is the unique interpreter extending `readInterp`. -/
378+
theorem toReaderM_unique {α : Type u} (g : FreeReader σ α → ReaderM σ α)
379+
(h : Interprets readInterp g) : g = toReaderM := h.eq
380+
381+
/-- Run a reader computation -/
382+
def run (comp : FreeReader σ α) (s₀ : σ) : α :=
383+
match comp with
384+
| .pure a => a
385+
| .liftBind ReaderF.read a => run (a s₀) s₀
386+
387+
/--
388+
The canonical interpreter `toReaderM` derived from `liftM` agrees with the hand-written
389+
recursive interpreter `run` for `FreeReader` -/
390+
@[simp]
391+
theorem run_toReaderM {α : Type u} (comp : FreeReader σ α) (s : σ) :
392+
(toReaderM comp).run s = run comp s := by
393+
induction comp generalizing s with
394+
| pure a => rfl
395+
| liftBind op cont ih =>
396+
cases op; apply ih
397+
398+
@[simp]
399+
lemma run_pure (a : α) (s₀ : σ) :
400+
run (.pure a : FreeReader σ α) s₀ = a := rfl
401+
402+
@[simp]
403+
lemma run_read (k : σ → FreeReader σ α) (s₀ : σ) :
404+
run (liftBind .read k) s₀ = run (k s₀) s₀ := rfl
405+
406+
instance instMonadWithReaderOf : MonadWithReaderOf σ (FreeReader σ) where
407+
withReader {α} f m :=
408+
let rec go : FreeReader σ α → FreeReader σ α
409+
| .pure a => .pure a
410+
| .liftBind .read cont =>
411+
.liftBind .read fun s => go (cont (f s))
412+
go m
413+
414+
@[simp] theorem run_withReader (f : σ → σ) (m : FreeReader σ α) (s : σ) :
415+
run (withTheReader σ f m) s = run m (f s) := by
416+
induction m generalizing s with
417+
| pure a => rfl
418+
| liftBind op cont ih =>
419+
cases op
420+
simpa [withTheReader, instMonadWithReaderOf, run] using (ih (f s) s)
421+
422+
end FreeReader
423+
356424
end FreeM
357425

358426
end Cslib

0 commit comments

Comments
 (0)