Skip to content

Commit b713d7d

Browse files
committed
feat(Data/PFunctor): add free monad of a polynomial functor
Port `PFunctor.FreeM` from VCV-io (ToMathlib/PFunctor/Free.lean). This defines the free monad on a polynomial functor (`PFunctor`), which extends the W-type construction with an extra `pure` constructor. Main definitions: - `PFunctor.FreeM`: inductive type with `pure` and `roll` constructors - `FreeM.lift` / `FreeM.liftA`: lifting from the base functor - `Monad` and `LawfulMonad` instances - `FreeM.inductionOn` / `FreeM.construct`: elimination principles - `FreeM.mapM`: canonical interpretation into any target monad Made-with: Cursor
1 parent 48e29fd commit b713d7d

2 files changed

Lines changed: 220 additions & 0 deletions

File tree

Cslib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public import Cslib.Foundations.Control.Monad.Free.Effects
4242
public import Cslib.Foundations.Control.Monad.Free.Fold
4343
public import Cslib.Foundations.Data.BiTape
4444
public import Cslib.Foundations.Data.FinFun
45+
public import Cslib.Foundations.Data.PFunctor.FreeM
4546
public import Cslib.Foundations.Data.HasFresh
4647
public import Cslib.Foundations.Data.Nat.Segment
4748
public import Cslib.Foundations.Data.OmegaSequence.Defs
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/-
2+
Copyright (c) 2024 Devon Tuma. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Quang Dao
5+
-/
6+
7+
module
8+
9+
public import Cslib.Init
10+
public import Mathlib.Data.PFunctor.Univariate.Basic
11+
12+
/-!
13+
# Free Monad of a Polynomial Functor
14+
15+
We define the free monad on a **polynomial functor** (`PFunctor`), and prove some basic properties.
16+
17+
The free monad `PFunctor.FreeM P` extends the W-type construction with an extra `pure`
18+
constructor, yielding a monad that is free over the polynomial functor `P`.
19+
20+
## Main Definitions
21+
22+
- `PFunctor.FreeM`: The free monad on a polynomial functor.
23+
- `PFunctor.FreeM.lift`: Lift an object of the base polynomial functor into the free monad.
24+
- `PFunctor.FreeM.liftA`: Lift a position of the base polynomial functor into the free monad.
25+
- `PFunctor.FreeM.mapM`: Canonical mapping of `FreeM P` into any other monad.
26+
- `PFunctor.FreeM.inductionOn`: Induction principle for `FreeM P`.
27+
- `PFunctor.FreeM.construct`: Dependent eliminator for `FreeM P`.
28+
-/
29+
30+
@[expose] public section
31+
32+
universe u v uA uB
33+
34+
namespace PFunctor
35+
36+
/-- The free monad on a polynomial functor.
37+
This extends the `W`-type construction with an extra `pure` constructor. -/
38+
inductive FreeM (P : PFunctor.{uA, uB}) : Type v → Type (max uA uB v)
39+
| pure {α} (x : α) : FreeM P α
40+
| roll {α} (a : P.A) (r : P.B a → FreeM P α) : FreeM P α
41+
deriving Inhabited
42+
43+
namespace FreeM
44+
45+
variable {P : PFunctor.{uA, uB}} {α β γ : Type v}
46+
47+
/-- Lift an object of the base polynomial functor into the free monad. -/
48+
@[always_inline, inline, reducible]
49+
def lift (x : P.Obj α) : FreeM P α := FreeM.roll x.1 (fun y ↦ FreeM.pure (x.2 y))
50+
51+
/-- Lift a position of the base polynomial functor into the free monad. -/
52+
@[always_inline, inline, reducible]
53+
def liftA (a : P.A) : FreeM P (P.B a) := lift ⟨a, id⟩
54+
55+
instance : MonadLift P (FreeM P) where
56+
monadLift x := FreeM.lift x
57+
58+
@[simp] lemma lift_ne_pure (x : P α) (y : α) :
59+
(lift x : FreeM P α) ≠ PFunctor.FreeM.pure y := by simp [lift]
60+
61+
@[simp] lemma pure_ne_lift (x : P α) (y : α) :
62+
PFunctor.FreeM.pure y ≠ (lift x : FreeM P α) := by simp [lift]
63+
64+
lemma monadLift_eq_lift (x : P.Obj α) : (x : FreeM P α) = FreeM.lift x := rfl
65+
66+
/-- Bind operator on `FreeM P` used in the monad definition. -/
67+
@[always_inline, inline]
68+
protected def bind : FreeM P α → (α → FreeM P β) → FreeM P β
69+
| FreeM.pure x, g => g x
70+
| FreeM.roll x r, g => FreeM.roll x (fun u ↦ FreeM.bind (r u) g)
71+
72+
@[simp]
73+
lemma bind_pure (x : α) (r : α → FreeM P β) :
74+
FreeM.bind (FreeM.pure x) r = r x := rfl
75+
76+
@[simp]
77+
lemma bind_roll (a : P.A) (r : P.B a → FreeM P β) (g : β → FreeM P γ) :
78+
FreeM.bind (FreeM.roll a r) g = FreeM.roll a (fun u ↦ FreeM.bind (r u) g) := rfl
79+
80+
@[simp]
81+
lemma bind_lift (x : P.Obj α) (r : α → FreeM P β) :
82+
FreeM.bind (FreeM.lift x) r = FreeM.roll x.1 (fun a ↦ r (x.2 a)) := rfl
83+
84+
@[simp] lemma bind_eq_pure_iff (x : FreeM P α) (y : α → FreeM P β) (y' : β) :
85+
FreeM.bind x y = FreeM.pure y' ↔ ∃ x', x = pure x' ∧ y x' = pure y' := by
86+
cases x <;> simp
87+
88+
@[simp] lemma pure_eq_bind_iff (x : FreeM P α) (y : α → FreeM P β) (y' : β) :
89+
FreeM.pure y' = FreeM.bind x y ↔ ∃ x', x = pure x' ∧ pure y' = y x' := by
90+
cases x <;> simp
91+
92+
instance : Monad (FreeM P) where
93+
pure := FreeM.pure
94+
bind := FreeM.bind
95+
96+
lemma monad_pure_def (x : α) : (pure x : FreeM P α) = FreeM.pure x := rfl
97+
98+
lemma monad_bind_def (x : FreeM P α) (g : α → FreeM P β) :
99+
x >>= g = FreeM.bind x g := rfl
100+
101+
instance : LawfulMonad (FreeM P) :=
102+
LawfulMonad.mk' (FreeM P)
103+
(fun x ↦ by
104+
induction x with
105+
| pure _ => rfl
106+
| roll a _ h => refine congr_arg (FreeM.roll a) (funext fun i ↦ h i))
107+
(fun x f ↦ rfl)
108+
(fun x f g ↦ by
109+
induction x with
110+
| pure _ => rfl
111+
| roll a _ h => refine congr_arg (FreeM.roll a) (funext fun i ↦ h i))
112+
113+
lemma pure_inj (x y : α) : FreeM.pure (P := P) x = FreeM.pure y ↔ x = y := by simp
114+
115+
@[simp] lemma roll_inj (x x' : P.A) (y : P.B x → P.FreeM α) (y' : P.B x' → P.FreeM α) :
116+
FreeM.roll x y = FreeM.roll x' y' ↔ ∃ h : x = x', h ▸ y = y' := by
117+
simp
118+
by_cases hx : x = x'
119+
· cases hx
120+
simp
121+
· simp [hx]
122+
123+
/-- Proving a predicate `C` of `FreeM P α` requires two cases:
124+
* `pure x` for some `x : α`
125+
* `roll x r h` for some `x : P.A`, `r : P.B x → FreeM P α`, and `h : ∀ y, C (r y)` -/
126+
@[elab_as_elim]
127+
protected def inductionOn {C : FreeM P α → Prop}
128+
(pure : ∀ x, C (pure x))
129+
(roll : (x : P.A) → (r : P.B x → FreeM P α) → (∀ y, C (r y)) → C (FreeM.roll x r)) :
130+
(oa : FreeM P α) → C oa
131+
| FreeM.pure x => pure x
132+
| FreeM.roll x r => roll x _ (fun u ↦ FreeM.inductionOn pure roll (r u))
133+
134+
section construct
135+
136+
/-- Dependent eliminator for `FreeM P`. -/
137+
@[elab_as_elim]
138+
protected def construct {C : FreeM P α → Type*}
139+
(pure : (x : α) → C (pure x))
140+
(roll : (x : P.A) → (r : P.B x → FreeM P α) → ((y : P.B x) → C (r y)) →
141+
C (FreeM.roll x r)) :
142+
(oa : FreeM P α) → C oa
143+
| .pure x => pure x
144+
| .roll x r => roll x _ (fun u ↦ FreeM.construct pure roll (r u))
145+
146+
variable {C : FreeM P α → Type*} (h_pure : (x : α) → C (pure x))
147+
(h_roll : (x : P.A) → (r : P.B x → FreeM P α) → ((y : P.B x) → C (r y)) →
148+
C (FreeM.roll x r))
149+
150+
@[simp]
151+
lemma construct_pure (y : α) : FreeM.construct h_pure h_roll (pure y) = h_pure y := rfl
152+
153+
@[simp]
154+
lemma construct_roll (x : P.A) (r : P.B x → FreeM P α) :
155+
(FreeM.construct h_pure h_roll (FreeM.roll x r) : C (FreeM.roll x r)) =
156+
(h_roll x r (fun u => FreeM.construct h_pure h_roll (r u))) := rfl
157+
158+
end construct
159+
160+
section mapM
161+
162+
variable {m : Type uB → Type v} {α : Type uB}
163+
164+
/-- Canonical mapping of `FreeM P` into any other monad, given a map `s : (a : P.A) → m (P.B a)`.
165+
-/
166+
protected def mapM [Pure m] [Bind m] (s : (a : P.A) → m (P.B a)) : FreeM P α → m α
167+
| .pure a => Pure.pure a
168+
| .roll a r => (s a) >>= (fun u ↦ (r u).mapM s)
169+
170+
variable [Monad m] (s : (a : P.A) → m (P.B a))
171+
172+
@[simp]
173+
lemma mapM_pure' (x : α) : (FreeM.pure x : FreeM P α).mapM s = Pure.pure x := rfl
174+
175+
@[simp]
176+
lemma mapM_roll (x : P.A) (r : P.B x → FreeM P α) :
177+
(FreeM.roll x r).mapM s = s x >>= fun u => (r u).mapM s := rfl
178+
179+
@[simp] lemma mapM_pure (x : α) : (Pure.pure x : FreeM P α).mapM s = Pure.pure x := rfl
180+
181+
variable [LawfulMonad m]
182+
183+
@[simp]
184+
lemma mapM_bind {α β : Type uB} (x : FreeM P α) (y : α → FreeM P β) :
185+
(FreeM.bind x y).mapM s = x.mapM s >>= fun u => (y u).mapM s := by
186+
induction x using FreeM.inductionOn with
187+
| pure _ => simp
188+
| roll x r h => simp [h]
189+
190+
@[simp]
191+
lemma mapM_bind' {α β : Type uB} (x : FreeM P α) (y : α → FreeM P β) :
192+
(x >>= y).mapM s = x.mapM s >>= fun u => (y u).mapM s :=
193+
mapM_bind _ _ _
194+
195+
@[simp]
196+
lemma mapM_map {α β : Type uB} (x : FreeM P α) (f : α → β) :
197+
FreeM.mapM s (f <$> x) = f <$> FreeM.mapM s x := by
198+
simp [← bind_pure_comp]
199+
200+
@[simp]
201+
lemma mapM_seq {α β : Type uB}
202+
(s : (a : P.A) → m (P.B a)) (x : FreeM P (α → β)) (y : FreeM P α) :
203+
FreeM.mapM s (x <*> y) = (FreeM.mapM s x) <*> (FreeM.mapM s y) := by
204+
simp [seq_eq_bind_map]
205+
206+
@[simp]
207+
lemma mapM_lift (s : (a : P.A) → m (P.B a)) (x : P.Obj α) :
208+
FreeM.mapM s (FreeM.lift x) = s x.1 >>= (fun u ↦ (pure (x.2 u)).mapM s) := by
209+
simp [FreeM.mapM]
210+
211+
@[simp]
212+
lemma mapM_liftA (s : (a : P.A) → m (P.B a)) (x : P.A) :
213+
FreeM.mapM s (FreeM.liftA x) = s x := by simp [liftA]
214+
215+
end mapM
216+
217+
end FreeM
218+
219+
end PFunctor

0 commit comments

Comments
 (0)