Typeclass for a type F
with an injective map to A ≃ B
#
This typeclass is primarily for use by isomorphisms like MonoidEquiv
and LinearEquiv
.
Basic usage of EquivLike
#
A typical type of morphisms should be declared as:
structure MyIso (A B : Type*) [MyClass A] [MyClass B]
extends Equiv A B :=
(map_op' : ∀ {x y : A}, toFun (MyClass.op x y) = MyClass.op (toFun x) (toFun y))
namespace MyIso
variables (A B : Type*) [MyClass A] [MyClass B]
-- This instance is optional if you follow the "Isomorphism class" design below:
instance : EquivLike (MyIso A B) A (λ _, B) :=
{ coe := MyIso.toEquiv.toFun,
inv := MyIso.toEquiv.invFun,
left_inv := MyIso.toEquiv.left_inv,
right_inv := MyIso.toEquiv.right_inv,
coe_injective' := λ f g h, by cases f; cases g; congr' }
/-- Helper instance for when there's too many metavariables to apply `EquivLike.coe` directly. -/
instance : CoeFun (MyIso A B) := DFunLike.instCoeFunForAll
@[ext] theorem ext {f g : MyIso A B} (h : ∀ x, f x = g x) : f = g := DFunLike.ext f g h
/-- Copy of a `MyIso` with a new `toFun` equal to the old one. Useful to fix definitional
equalities. -/
protected def copy (f : MyIso A B) (f' : A → B) (f_inv : B → A) (h : f' = ⇑f) : MyIso A B :=
{ toFun := f',
invFun := f_inv,
left_inv := h.symm ▸ f.left_inv,
right_inv := h.symm ▸ f.right_inv,
map_op' := h.symm ▸ f.map_op' }
end MyIso
This file will then provide a CoeFun
instance and various
extensionality and simp lemmas.
Isomorphism classes extending EquivLike
#
The EquivLike
design provides further benefits if you put in a bit more work.
The first step is to extend EquivLike
to create a class of those types satisfying
the axioms of your new type of isomorphisms.
Continuing the example above:
/-- `MyIsoClass F A B` states that `F` is a type of `MyClass.op`-preserving morphisms.
You should extend this class when you extend `MyIso`. -/
class MyIsoClass (F : Type*) (A B : outParam <| Type*) [MyClass A] [MyClass B]
extends EquivLike F A (λ _, B), MyHomClass F A B
end
-- You can replace `MyIso.EquivLike` with the below instance:
instance : MyIsoClass (MyIso A B) A B :=
{ coe := MyIso.toFun,
inv := MyIso.invFun,
left_inv := MyIso.left_inv,
right_inv := MyIso.right_inv,
coe_injective' := λ f g h, by cases f; cases g; congr',
map_op := MyIso.map_op' }
-- [Insert `CoeFun`, `ext` and `copy` here]
The second step is to add instances of your new MyIsoClass
for all types extending MyIso
.
Typically, you can just declare a new class analogous to MyIsoClass
:
structure CoolerIso (A B : Type*) [CoolClass A] [CoolClass B]
extends MyIso A B :=
(map_cool' : toFun CoolClass.cool = CoolClass.cool)
section
set_option old_structure_cmd true
class CoolerIsoClass (F : Type*) (A B : outParam <| Type*) [CoolClass A] [CoolClass B]
extends MyIsoClass F A B :=
(map_cool : ∀ (f : F), f CoolClass.cool = CoolClass.cool)
end
@[simp] lemma map_cool {F A B : Type*} [CoolClass A] [CoolClass B] [CoolerIsoClass F A B]
(f : F) : f CoolClass.cool = CoolClass.cool :=
CoolerIsoClass.map_cool
instance : CoolerIsoClass (CoolerIso A B) A B :=
{ coe := CoolerIso.toFun,
coe_injective' := λ f g h, by cases f; cases g; congr',
map_op := CoolerIso.map_op',
map_cool := CoolerIso.map_cool' }
-- [Insert `CoeFun`, `ext` and `copy` here]
Then any declaration taking a specific type of morphisms as parameter can instead take the class you just defined:
-- Compare with: lemma do_something (f : MyIso A B) : sorry := sorry
lemma do_something {F : Type*} [MyIsoClass F A B] (f : F) : sorry := sorry
This means anything set up for MyIso
s will automatically work for CoolerIsoClass
es,
and defining CoolerIsoClass
only takes a constant amount of effort,
instead of linearly increasing the work per MyIso
-related declaration.
The class EquivLike E α β
expresses that terms of type E
have an
injective coercion to bijections between α
and β
.
Note that this does not directly extend FunLike
, nor take FunLike
as a parameter,
so we can state coe_injective'
in a nicer way.
This typeclass is used in the definition of the homomorphism typeclasses,
such as ZeroEquivClass
, MulEquivClass
, MonoidEquivClass
, ....
- coe : E → α → β
The coercion to a function in the forward direction.
- inv : E → β → α
The coercion to a function in the backwards direction.
- left_inv : ∀ (e : E), Function.LeftInverse (EquivLike.inv e) (EquivLike.coe e)
The coercions are left inverses.
- right_inv : ∀ (e : E), Function.RightInverse (EquivLike.inv e) (EquivLike.coe e)
The coercions are right inverses.
- coe_injective' : ∀ (e g : E), EquivLike.coe e = EquivLike.coe g → EquivLike.inv e = EquivLike.inv g → e = g
The two coercions to functions are jointly injective.
Instances
Equations
- EquivLike.toFunLike = { coe := EquivLike.coe, coe_injective' := (_ : ∀ (e g : E), EquivLike.coe e = EquivLike.coe g → e = g) }
Equations
- (_ : EmbeddingLike E α β) = (_ : EmbeddingLike E α β)
This lemma is only supposed to be used in the generic context, when working with instances
of classes extending EquivLike
.
For concrete isomorphism types such as Equiv
, you should use Equiv.symm_apply_apply
or its equivalent.
TODO: define a generic form of Equiv.symm
.
This lemma is only supposed to be used in the generic context, when working with instances
of classes extending EquivLike
.
For concrete isomorphism types such as Equiv
, you should use Equiv.apply_symm_apply
or its equivalent.
TODO: define a generic form of Equiv.symm
.
This is not an instance to avoid slowing down every single Subsingleton
typeclass search.