For performance reasons, it is inadvisable to use user_attribute.get_param.
The parameter is stored as a reflected expression. When calling get_param,
the stored parameter is evaluated using eval_expr, which first compiles the
expression into VM bytecode. The unevaluated expression is available using
user_attribute.get_param_untyped.
In particular, user_attribute.get_param MUST NEVER BE USED in the
implementation of an attribute cache. This is because calling eval_expr
disables the attribute cache.
There are several possible workarounds:
- Set a different attribute depending on the parameter.
- Use your own evaluation function instead of
eval_expr, such as e.g.expr.to_nat. - Write your own
has_reflect Paraminstance (using a more efficient serialization format). Theuser_attributecode unfortunately checks whether the expression has the correct type, but you can use`(id %%e : Param)to pretend that your expressionehas typeParam.
For performance reasons, the parameters of the @[ext] attribute are stored
in two auxiliary attributes:
Tag lemmas of the form:
@[ext]
lemma my_collection.ext (a b : my_collection)
(h : ∀ x, a.lookup x = b.lookup y) :
a = b := ...
The attribute indexes extensionality lemma using the type of the
objects (i.e. my_collection) which it gets from the statement of
the lemma. In some cases, the same lemma can be used to state the
extensionality of multiple types that are definitionally equivalent.
Also, the following:
@[ext]
lemma my_collection.ext (a b : my_collection)
(h : ∀ x, a.lookup x = b.lookup y) :
a = b := ...
is equivalent to
@[ext my_collection]
lemma my_collection.ext (a b : my_collection)
(h : ∀ x, a.lookup x = b.lookup y) :
a = b := ...
This allows us specify type synonyms along with the type that is referred to in the lemma statement.
@[ext, ext my_type_synonym]
lemma my_collection.ext (a b : my_collection)
(h : ∀ x, a.lookup x = b.lookup y) :
a = b := ...
The ext attribute can be applied to a structure to generate its extensionality lemmas:
@[ext]
structure foo (α : Type*) :=
(x y : ℕ)
(z : {z // z < x})
(k : α)
(h : x < y)
will generate:
@[ext] lemma foo.ext : ∀ {α : Type u_1} (x y : foo α),
x.x = y.x → x.y = y.y → x.z == y.z → x.k = y.k → x = y
lemma foo.ext_iff : ∀ {α : Type u_1} (x y : foo α),
x = y ↔ x.x = y.x ∧ x.y = y.y ∧ x.z == y.z ∧ x.k = y.k
When possible, ext lemmas are stated without a full set of arguments. As an example, for bundled
homs f, g, and of, f.comp of = g.comp of → f = g is a better ext lemma than
(∀ x, f (of x) = g (of x)) → f = g, as the former allows a second type-specific extensionality
lemmas to be applied to f.comp of = g.comp of.
If the domain of of is ℕ or ℤ and of is a ring_hom, such a lemma could then make the goal
f (of 1) = g (of 1).
For bundled morphisms, there is a ext lemma that always applies of the form
(∀ x, ⇑f x = ⇑g x) → f = g. When adding type-specific ext lemmas like the one above, we want
these to be tried first. This happens automatically since the type-specific lemmas are inevitably
defined later.
-
ext1 idselects and apply one extensionality lemma (with attributeext), usingid, if provided, to name a local constant introduced by the lemma. Ifidis omitted, the local constant is named automatically, as perintro. -
extapplies as many extensionality lemmas as possible; -
ext ids, withidsa list of identifiers, finds extensionality lemmas and applies them until it runs out of identifiers inidsto name the local constants. -
extcan also be given anrcasespattern in place of an identifier. This will destruct the introduced local constant.
- Placing a
?afterext/ext1(e.g.ext? i ⟨a,b⟩ : 3) will display a sequence of tactic applications that can replace the call toext/ext1. set_option trace.ext truewill trace every attempted lemma application, along with the time it takes for the application to succeed or fail. This is useful for debugging slowextcalls.
When trying to prove:
α β : Type,
f g : α → set β
⊢ f = g
applying ext x y yields:
α β : Type,
f g : α → set β,
x : α,
y : β
⊢ y ∈ f x ↔ y ∈ g x
by applying functional extensionality and set extensionality.
When trying to prove:
α β γ : Type
f g : α × β → γ
⊢ f = g
applying ext ⟨a, b⟩ yields:
α β γ : Type,
f g : α × β → γ,
a : α,
b : β
⊢ f (a, b) = g (a, b)
by applying functional extensionality and destructing the introduced pair.
In the previous example, applying ext? ⟨a,b⟩ will produce the trace message:
Try this: apply funext, rintro ⟨a, b⟩
A maximum depth can be provided with ext x y z : 3.