Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 5e3d465

Browse files
committed
feat(category_theory/category/PartialFun): The category of types with partial functions (#11866)
Define `PartialFun`, the category of types with partial functions, and show its equivalence with `Pointed`.
1 parent 3c78d00 commit 5e3d465

File tree

3 files changed

+166
-14
lines changed

3 files changed

+166
-14
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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 _

src/category_theory/category/Pointed.lean

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ instance concrete_category : concrete_category Pointed :=
6969
{ forget := { obj := Pointed.X, map := @hom.to_fun },
7070
forget_faithful := ⟨@hom.ext⟩ }
7171

72+
/-- Constructs a isomorphism between pointed types from an equivalence that preserves the point
73+
between them. -/
74+
@[simps] def iso.mk {α β : Pointed} (e : α ≃ β) (he : e α.point = β.point) : α ≅ β :=
75+
{ hom := ⟨e, he⟩,
76+
inv := ⟨e.symm, e.symm_apply_eq.2 he.symm⟩,
77+
hom_inv_id' := Pointed.hom.ext _ _ e.symm_comp_self,
78+
inv_hom_id' := Pointed.hom.ext _ _ e.self_comp_symm }
79+
7280
end Pointed
7381

7482
/-- `option` as a functor from types to pointed types. This is the free functor. -/

src/data/subtype.lean

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,23 +91,18 @@ ext_iff
9191
theorem coe_eq_iff {a : {a // p a}} {b : α} : ↑a = b ↔ ∃ h, a = ⟨b, h⟩ :=
9292
⟨λ h, h ▸ ⟨a.2, (coe_eta _ _).symm⟩, λ ⟨hb, ha⟩, ha.symm ▸ rfl⟩
9393

94-
theorem coe_injective : injective (coe : subtype p → α) :=
95-
λ a b, subtype.ext
94+
lemma coe_injective : injective (coe : subtype p → α) := λ a b, subtype.ext
95+
lemma val_injective : injective (@val _ p) := coe_injective
96+
lemma coe_inj {a b : subtype p} : (a : α) = b ↔ a = b := coe_injective.eq_iff
97+
lemma val_inj {a b : subtype p} : a.val = b.val ↔ a = b := coe_inj
9698

97-
theorem val_injective : injective (@val _ p) :=
98-
coe_injective
99+
@[simp] lemma _root_.exists_eq_subtype_mk_iff {a : subtype p} {b : α} :
100+
(∃ h : p b, a = subtype.mk b h) ↔ ↑a = b :=
101+
coe_eq_iff.symm
99102

100-
@[simp] lemma _root_.exists_subtype_mk_eq_iff {p : α → Prop} {a : subtype p} {b : α} :
103+
@[simp] lemma _root_.exists_subtype_mk_eq_iff {a : subtype p} {b : α} :
101104
(∃ h : p b, subtype.mk b h = a) ↔ b = a :=
102-
begin
103-
refine ⟨λ ⟨h, ha⟩, congr_arg subtype.val ha, λ h, _⟩,
104-
rw h,
105-
exact ⟨a.2, subtype.coe_eta _ _⟩,
106-
end
107-
108-
@[simp] lemma _root_.exists_eq_subtype_mk_iff {p : α → Prop} {a : subtype p} {b : α} :
109-
(∃ h : p b, a = subtype.mk b h) ↔ ↑a = b :=
110-
by simp only [@eq_comm _ a, exists_subtype_mk_eq_iff, @eq_comm _ _ b]
105+
by simp only [@eq_comm _ b, exists_eq_subtype_mk_iff, @eq_comm _ _ a]
111106

112107
/-- Restrict a (dependent) function to a subtype -/
113108
def restrict {α} {β : α → Type*} (f : Π x, β x) (p : α → Prop) (x : subtype p) : β x.1 :=

0 commit comments

Comments
 (0)