|
| 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