forked from leanprover-community/mathlib4
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathULiftable.lean
More file actions
185 lines (140 loc) · 7.92 KB
/
ULiftable.lean
File metadata and controls
185 lines (140 loc) · 7.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/-
Copyright (c) 2020 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon
-/
module
public import Mathlib.Control.Monad.Basic
public import Mathlib.Control.Monad.Cont
public import Mathlib.Control.Monad.Writer
public import Mathlib.Logic.Equiv.Basic
public import Mathlib.Logic.Equiv.Functor
public import Mathlib.Control.Lawful
/-!
# Universe lifting for type families
Some functors such as `Option` and `List` are universe polymorphic. Unlike
type polymorphism where `Option α` is a function application and reasoning and
generalizations that apply to functions can be used, `Option.{u}` and `Option.{v}`
are not one function applied to two universe names but one polymorphic definition
instantiated twice. This means that whatever works on `Option.{u}` is hard
to transport over to `Option.{v}`. `ULiftable` is an attempt at improving the situation.
`ULiftable Option.{u} Option.{v}` gives us a generic and composable way to use
`Option.{u}` in a context that requires `Option.{v}`. It is often used in tandem with
`ULift` but the two are purposefully decoupled.
## Main definitions
* `ULiftable` class
## Tags
universe polymorphism functor
-/
@[expose] public section
universe v u₀ u₁ v₀ v₁ v₂ w w₀ w₁
variable {s : Type u₀} {s' : Type u₁} {r r' w w' : Type*}
/-- Given a universe polymorphic type family `M.{u} : Type u₁ → Type
u₂`, this class convert between instantiations, from
`M.{u} : Type u₁ → Type u₂` to `M.{v} : Type v₁ → Type v₂` and back.
`f` is an outParam, because `g` can almost always be inferred from the current monad.
At any rate, the lift should be unique, as the intent is to only lift the same constants with
different universe parameters. -/
class ULiftable (f : outParam (Type u₀ → Type u₁)) (g : Type v₀ → Type v₁) where
congr {α β} : α ≃ β → f α ≃ g β
namespace ULiftable
/-- Not an instance as it is incompatible with `outParam`. In practice it seems not to be needed
anyway. -/
abbrev symm (f : Type u₀ → Type u₁) (g : Type v₀ → Type v₁) [ULiftable f g] : ULiftable g f where
congr e := (ULiftable.congr e.symm).symm
instance refl (f : Type u₀ → Type u₁) [Functor f] [LawfulFunctor f] : ULiftable f f where
congr e := Functor.mapEquiv _ e
/-- The most common practical use `ULiftable` (together with `down`), the function `up.{v}` takes
`x : M.{u} α` and lifts it to `M.{max u v} (ULift.{v} α)` -/
abbrev up {f : Type u₀ → Type u₁} {g : Type max u₀ v → Type v₁} [ULiftable f g] {α} :
f α → g (ULift.{v} α) :=
(ULiftable.congr Equiv.ulift.symm).toFun
/-- The most common practical use of `ULiftable` (together with `up`), the function `down.{v}` takes
`x : M.{max u v} (ULift.{v} α)` and lowers it to `M.{u} α` -/
abbrev down {f : Type u₀ → Type u₁} {g : Type max u₀ v → Type v₁} [ULiftable f g] {α} :
g (ULift.{v} α) → f α :=
(ULiftable.congr Equiv.ulift.symm).invFun
/-- convenient shortcut to avoid manipulating `ULift` -/
def adaptUp (F : Type v₀ → Type v₁) (G : Type max v₀ u₀ → Type u₁) [ULiftable F G] [Monad G] {α β}
(x : F α) (f : α → G β) : G β :=
up x >>= f ∘ ULift.down.{u₀}
/-- convenient shortcut to avoid manipulating `ULift` -/
def adaptDown {F : Type max u₀ v₀ → Type u₁} {G : Type v₀ → Type v₁} [L : ULiftable G F] [Monad F]
{α β} (x : F α) (f : α → G β) : G β :=
@down.{max u₀ v₀} G F L β <| x >>= @up.{max u₀ v₀} G F L β ∘ f
/-- map function that moves up universes -/
def upMap {F : Type u₀ → Type u₁} {G : Type max u₀ v₀ → Type v₁} [ULiftable F G] [Functor G]
{α β} (f : α → β) (x : F α) : G β :=
Functor.map (f ∘ ULift.down.{v₀}) (up x)
/-- map function that moves down universes -/
def downMap {F : Type max u₀ v₀ → Type u₁} {G : Type u₀ → Type v₁} [ULiftable G F]
[Functor F] {α β} (f : α → β) (x : F α) : G β :=
down (Functor.map (ULift.up.{v₀} ∘ f) x : F (ULift β))
/-- A version of `up` for a `PUnit` return type. -/
abbrev up' {f : Type u₀ → Type u₁} {g : Type v₀ → Type v₁} [ULiftable f g] :
f PUnit → g PUnit :=
ULiftable.congr Equiv.punitEquivPUnit
/-- A version of `down` for a `PUnit` return type. -/
abbrev down' {f : Type u₀ → Type u₁} {g : Type v₀ → Type v₁} [ULiftable f g] :
g PUnit → f PUnit :=
(ULiftable.congr Equiv.punitEquivPUnit).symm
theorem up_down {f : Type u₀ → Type u₁} {g : Type max u₀ v₀ → Type v₁} [ULiftable f g] {α}
(x : g (ULift.{v₀} α)) : up (down x : f α) = x :=
(ULiftable.congr Equiv.ulift.symm).right_inv _
theorem down_up {f : Type u₀ → Type u₁} {g : Type max u₀ v₀ → Type v₁} [ULiftable f g] {α}
(x : f α) : down (up x : g (ULift.{v₀} α)) = x :=
(ULiftable.congr Equiv.ulift.symm).left_inv _
end ULiftable
open ULift
instance instULiftableId : ULiftable Id Id where
congr F := F
/-- for specific state types, this function helps to create a uliftable instance -/
def StateT.uliftable' {m : Type u₀ → Type v₀} {m' : Type u₁ → Type v₁} [ULiftable m m']
(F : s ≃ s') : ULiftable (StateT s m) (StateT s' m') where
congr G :=
StateT.equiv <| Equiv.piCongr F fun _ => ULiftable.congr <| Equiv.prodCongr G F
instance {m m'} [ULiftable m m'] : ULiftable (StateT s m) (StateT (ULift s) m') :=
StateT.uliftable' Equiv.ulift.symm
instance StateT.instULiftableULiftULift {m m'} [ULiftable m m'] :
ULiftable (StateT (ULift.{max v₀ u₀} s) m) (StateT (ULift.{max v₁ u₀} s) m') :=
StateT.uliftable' <| Equiv.ulift.trans Equiv.ulift.symm
/-- for specific reader monads, this function helps to create a uliftable instance -/
def ReaderT.uliftable' {m m'} [ULiftable m m'] (F : s ≃ s') :
ULiftable (ReaderT s m) (ReaderT s' m') where
congr G := ReaderT.equiv <| Equiv.piCongr F fun _ => ULiftable.congr G
instance {m m'} [ULiftable m m'] : ULiftable (ReaderT s m) (ReaderT (ULift s) m') :=
ReaderT.uliftable' Equiv.ulift.symm
instance ReaderT.instULiftableULiftULift {m m'} [ULiftable m m'] :
ULiftable (ReaderT (ULift.{max v₀ u₀} s) m) (ReaderT (ULift.{max v₁ u₀} s) m') :=
ReaderT.uliftable' <| Equiv.ulift.trans Equiv.ulift.symm
/-- for specific continuation passing monads, this function helps to create a uliftable instance -/
def ContT.uliftable' {m m'} [ULiftable m m'] (F : r ≃ r') :
ULiftable (ContT r m) (ContT r' m') where
congr := ContT.equiv (ULiftable.congr F)
instance {s m m'} [ULiftable m m'] : ULiftable (ContT s m) (ContT (ULift s) m') :=
ContT.uliftable' Equiv.ulift.symm
instance ContT.instULiftableULiftULift {m m'} [ULiftable m m'] :
ULiftable (ContT (ULift.{max v₀ u₀} s) m) (ContT (ULift.{max v₁ u₀} s) m') :=
ContT.uliftable' <| Equiv.ulift.trans Equiv.ulift.symm
/-- for specific writer monads, this function helps to create a uliftable instance -/
def WriterT.uliftable' {m m'} [ULiftable m m'] (F : w ≃ w') :
ULiftable (WriterT w m) (WriterT w' m') where
congr G := WriterT.equiv <| ULiftable.congr <| Equiv.prodCongr G F
instance {m m'} [ULiftable m m'] : ULiftable (WriterT s m) (WriterT (ULift s) m') :=
WriterT.uliftable' Equiv.ulift.symm
instance WriterT.instULiftableULiftULift {m m'} [ULiftable m m'] :
ULiftable (WriterT (ULift.{max v₀ u₀} s) m) (WriterT (ULift.{max v₁ u₀} s) m') :=
WriterT.uliftable' <| Equiv.ulift.trans Equiv.ulift.symm
instance Except.instULiftable {ε : Type u₀} :
ULiftable (Except.{u₀, v₁} ε) (Except.{u₀, v₂} ε) where
congr e :=
{ toFun := Except.map e
invFun := Except.map e.symm
left_inv := fun f => by cases f <;> simp [Except.map]
right_inv := fun f => by cases f <;> simp [Except.map] }
instance Option.instULiftable : ULiftable Option.{u₀} Option.{u₁} where
congr e :=
{ toFun := Option.map e
invFun := Option.map e.symm
left_inv := fun f => by cases f <;> simp
right_inv := fun f => by cases f <;> simp }