The equiv_rw
tactic transports goals or hypotheses along equivalences. #
The basic syntax is equiv_rw e
, where e : α ≃ β
is an equivalence.
This will try to replace occurrences of α
in the goal with β
, for example
transforming
⊢ α
to⊢ β
,⊢ option α
to⊢ option β
⊢ {a // P}
to{b // P (⇑(equiv.symm e) b)}
The tactic can also be used to rewrite hypotheses, using the syntax equiv_rw e at h
.
Implementation details #
The main internal function is equiv_rw_type e t
,
which attempts to turn an expression e : α ≃ β
into a new equivalence with left hand side t
.
As an example, with t = option α
, it will generate functor.map_equiv option e
.
This is achieved by generating a new synthetic goal %%t ≃ _
,
and calling solve_by_elim
with an appropriate set of congruence lemmas.
To avoid having to specify the relevant congruence lemmas by hand,
we mostly rely on equiv_functor.map_equiv
and bifunctor.map_equiv
along with some structural congruence lemmas such as
equiv.arrow_congr'
,equiv.subtype_equiv_of_subtype'
,equiv.sigma_congr_left'
, andequiv.Pi_congr_left'
.
The main equiv_rw
function, when operating on the goal, simply generates a new equivalence e'
with left hand side matching the target, and calls apply e'.inv_fun
.
When operating on a hypothesis x : α
, we introduce a new fact h : x = e.symm (e x)
, revert this,
and then attempt to generalize
, replacing all occurrences of e x
with a new constant y
, before
intro
ing and subst
ing h
, and renaming y
back to x
.
Future improvements #
In a future PR I anticipate that derive equiv_functor
should work on many examples,
(internally using transport
, which is in turn based on equiv_rw
)
and we can incrementally bootstrap the strength of equiv_rw
.
An ambitious project might be to add equiv_rw!
,
a tactic which, when failing to find appropriate equiv_functor
instances,
attempts to derive
them on the spot.
For now equiv_rw
is entirely based on equiv
,
but the framework can readily be generalised to also work with other types of equivalences,
for example specific notations such as ring equivalence (≃+*
),
or general categorical isomorphisms (≅
).
This will allow us to transport across more general types of equivalences, but this will wait for another subsequent PR.