|
| 1 | +/- |
| 2 | +Copyright (c) 2022 Yaël Dillies. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Yaël Dillies |
| 5 | +-/ |
| 6 | +import category_theory.category.Pointed |
| 7 | +import data.pfun |
| 8 | + |
| 9 | +/-! |
| 10 | +# The category of types with partial functions |
| 11 | +
|
| 12 | +This defines `PartialFun`, the category of types equipped with partial functions. |
| 13 | +
|
| 14 | +This category is classically equivalent to the category of pointed types. The reason it doesn't hold |
| 15 | +constructively stems from the difference between `part` and `option`. Both can model partial |
| 16 | +functions, but the latter forces a decidable domain. |
| 17 | +
|
| 18 | +Precisely, `PartialFun_to_Pointed` turns a partial function `α →. β` into a function |
| 19 | +`option α → option β` by sending to `none` the undefined values (and `none` to `none`). But being |
| 20 | +defined is (generally) undecidable while being sent to `none` is decidable. So it can't be |
| 21 | +constructive. |
| 22 | +
|
| 23 | +## References |
| 24 | +
|
| 25 | +* [nLab, *The category of sets and partial functions] |
| 26 | +[http://nlab-pages.s3.us-east-2.amazonaws.com/nlab/show/partial+function] |
| 27 | +-/ |
| 28 | + |
| 29 | +open category_theory option |
| 30 | + |
| 31 | +universes u |
| 32 | +variables {α β : Type*} |
| 33 | + |
| 34 | +/-- The category of types equipped with partial functions. -/ |
| 35 | +def PartialFun : Type* := Type* |
| 36 | + |
| 37 | +namespace PartialFun |
| 38 | + |
| 39 | +instance : has_coe_to_sort PartialFun Type* := ⟨id⟩ |
| 40 | + |
| 41 | +/-- Turns a type into a `PartialFun`. -/ |
| 42 | +@[nolint has_inhabited_instance] def of : Type* → PartialFun := id |
| 43 | + |
| 44 | +instance : inhabited PartialFun := ⟨Type*⟩ |
| 45 | + |
| 46 | +instance large_category : large_category.{u} PartialFun := |
| 47 | +{ hom := pfun, |
| 48 | + id := pfun.id, |
| 49 | + comp := λ X Y Z f g, g.comp f, |
| 50 | + id_comp' := @pfun.comp_id, |
| 51 | + comp_id' := @pfun.id_comp, |
| 52 | + assoc' := λ W X Y Z _ _ _, (pfun.comp_assoc _ _ _).symm } |
| 53 | + |
| 54 | +/-- Constructs a partial function isomorphism between types from an equivalence between them. -/ |
| 55 | +@[simps] def iso.mk {α β : PartialFun.{u}} (e : α ≃ β) : α ≅ β := |
| 56 | +{ hom := e, |
| 57 | + inv := e.symm, |
| 58 | + hom_inv_id' := (pfun.coe_comp _ _).symm.trans $ congr_arg coe e.symm_comp_self, |
| 59 | + inv_hom_id' := (pfun.coe_comp _ _).symm.trans $ congr_arg coe e.self_comp_symm } |
| 60 | + |
| 61 | +end PartialFun |
| 62 | + |
| 63 | +/-- The forgetful functor from `Type` to `PartialFun` which forgets that the maps are total. -/ |
| 64 | +def Type_to_PartialFun : Type.{u} ⥤ PartialFun := |
| 65 | +{ obj := id, |
| 66 | + map := @pfun.lift, |
| 67 | + map_comp' := λ _ _ _ _ _, pfun.coe_comp _ _ } |
| 68 | + |
| 69 | +instance : faithful Type_to_PartialFun := ⟨λ X Y, pfun.coe_injective⟩ |
| 70 | + |
| 71 | +/-- The functor which deletes the point of a pointed type. In return, this makes the maps partial. |
| 72 | +This the computable part of the equivalence `PartialFun_equiv_Pointed`. -/ |
| 73 | +def Pointed_to_PartialFun : Pointed.{u} ⥤ PartialFun := |
| 74 | +{ obj := λ X, {x : X // x ≠ X.point}, |
| 75 | + map := λ X Y f, pfun.to_subtype _ f.to_fun ∘ subtype.val, |
| 76 | + map_id' := λ X, pfun.ext $ λ a b, |
| 77 | + pfun.mem_to_subtype_iff.trans (subtype.coe_inj.trans part.mem_some_iff.symm), |
| 78 | + map_comp' := λ X Y Z f g, pfun.ext $ λ a c, begin |
| 79 | + refine (pfun.mem_to_subtype_iff.trans _).trans part.mem_bind_iff.symm, |
| 80 | + simp_rw [pfun.mem_to_subtype_iff, subtype.exists], |
| 81 | + refine ⟨λ h, ⟨f.to_fun a, λ ha, c.2 $ h.trans |
| 82 | + ((congr_arg g.to_fun ha : g.to_fun _ = _).trans g.map_point), rfl, h⟩, _⟩, |
| 83 | + rintro ⟨b, _, (rfl : b = _), h⟩, |
| 84 | + exact h, |
| 85 | + end } |
| 86 | + |
| 87 | +/-- The functor which maps undefined values to a new point. This makes the maps total and creates |
| 88 | +pointed types. This the noncomputable part of the equivalence `PartialFun_equiv_Pointed`. It can't |
| 89 | +be computable because `= option.none` is decidable while the domain of a general `part` isn't. -/ |
| 90 | +noncomputable def PartialFun_to_Pointed : PartialFun ⥤ Pointed := |
| 91 | +by classical; exact |
| 92 | +{ obj := λ X, ⟨option X, none⟩, |
| 93 | + map := λ X Y f, ⟨λ o, o.elim none (λ a, (f a).to_option), rfl⟩, |
| 94 | + map_id' := λ X, Pointed.hom.ext _ _ $ funext $ λ o, |
| 95 | + option.rec_on o rfl $ λ a, part.some_to_option _, |
| 96 | + map_comp' := λ X Y Z f g, Pointed.hom.ext _ _ $ funext $ λ o, option.rec_on o rfl $ λ a, |
| 97 | + part.bind_to_option _ _ } |
| 98 | + |
| 99 | +/-- The equivalence induced by `PartialFun_to_Pointed` and `Pointed_to_PartialFun`. |
| 100 | +`part.equiv_option` made functorial. -/ |
| 101 | +@[simps] noncomputable def PartialFun_equiv_Pointed : PartialFun.{u} ≌ Pointed := |
| 102 | +by classical; exact |
| 103 | +equivalence.mk PartialFun_to_Pointed Pointed_to_PartialFun |
| 104 | + (nat_iso.of_components (λ X, PartialFun.iso.mk |
| 105 | + { to_fun := λ a, ⟨some a, some_ne_none a⟩, |
| 106 | + inv_fun := λ a, get $ ne_none_iff_is_some.1 a.2, |
| 107 | + left_inv := λ a, get_some _ _, |
| 108 | + right_inv := λ a, by simp only [subtype.val_eq_coe, some_get, subtype.coe_eta] }) $ λ X Y f, |
| 109 | + pfun.ext $ λ a b, begin |
| 110 | + unfold_projs, |
| 111 | + dsimp, |
| 112 | + rw part.bind_some, |
| 113 | + refine (part.mem_bind_iff.trans _).trans pfun.mem_to_subtype_iff.symm, |
| 114 | + obtain ⟨b | b, hb⟩ := b, |
| 115 | + { exact (hb rfl).elim }, |
| 116 | + dsimp, |
| 117 | + simp_rw [part.mem_some_iff, subtype.mk_eq_mk, exists_prop, some_inj, exists_eq_right'], |
| 118 | + refine part.mem_to_option.symm.trans _, |
| 119 | + convert eq_comm, |
| 120 | + convert rfl, |
| 121 | + end) |
| 122 | + (nat_iso.of_components (λ X, Pointed.iso.mk |
| 123 | + { to_fun := λ a, a.elim X.point subtype.val, |
| 124 | + inv_fun := λ a, if h : a = X.point then none else some ⟨_, h⟩, |
| 125 | + left_inv := λ a, option.rec_on a (dif_pos rfl) $ λ a, (dif_neg a.2).trans $ |
| 126 | + by simp only [option.elim, subtype.val_eq_coe, subtype.coe_eta], |
| 127 | + right_inv := λ a, begin |
| 128 | + change option.elim (dite _ _ _) _ _ = _, |
| 129 | + split_ifs, |
| 130 | + { rw h, refl }, |
| 131 | + { refl } |
| 132 | + end } rfl) $ λ X Y f, Pointed.hom.ext _ _ $ funext $ λ a, option.rec_on a f.map_point.symm $ |
| 133 | + λ a, begin |
| 134 | + change option.elim (option.elim _ _ _) _ _ = _, |
| 135 | + rw [option.elim, part.elim_to_option], |
| 136 | + split_ifs, |
| 137 | + { refl }, |
| 138 | + { exact eq.symm (of_not_not h) } |
| 139 | + end) |
| 140 | + |
| 141 | +/-- Forgetting that maps are total and making them total again by adding a point is the same as just |
| 142 | +adding a point. -/ |
| 143 | +@[simps] noncomputable def Type_to_PartialFun_iso_PartialFun_to_Pointed : |
| 144 | + Type_to_PartialFun ⋙ PartialFun_to_Pointed ≅ Type_to_Pointed := |
| 145 | +nat_iso.of_components (λ X, { hom := ⟨id, rfl⟩, |
| 146 | + inv := ⟨id, rfl⟩, |
| 147 | + hom_inv_id' := rfl, |
| 148 | + inv_hom_id' := rfl }) $ λ X Y f, |
| 149 | + Pointed.hom.ext _ _ $ funext $ λ a, option.rec_on a rfl $ λ a, by convert part.some_to_option _ |
0 commit comments