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 Param
instance (using a more efficient serialization format). Theuser_attribute
code unfortunately checks whether the expression has the correct type, but you can use`(id %%e : Param)
to pretend that your expressione
has 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 id
selects and apply one extensionality lemma (with attributeext
), usingid
, if provided, to name a local constant introduced by the lemma. Ifid
is omitted, the local constant is named automatically, as perintro
. -
ext
applies as many extensionality lemmas as possible; -
ext ids
, withids
a list of identifiers, finds extensionality lemmas and applies them until it runs out of identifiers inids
to name the local constants. -
ext
can also be given anrcases
pattern 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 true
will trace every attempted lemma application, along with the time it takes for the application to succeed or fail. This is useful for debugging slowext
calls.
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
.