simps attribute #
This file defines the @[simps]
attribute, to automatically generate simp
lemmas
reducing a definition when projections are applied to it.
Implementation Notes #
There are three attributes being defined here
@[simps]
is the attribute for objects of a structure or instances of a class. It will automatically generate simplification lemmas for each projection of the object/instance that contains data. See the doc strings forsimps_attr
andsimps_cfg
for more details and configuration options.@[_simps_str]
is automatically added to structures that have been used in@[simps]
at least once. This attribute contains the data of the projections used for this structure by all following invocations of@[simps]
.@[notation_class]
should be added to all classes that define notation, likehas_mul
andhas_zero
. This specifies that the projections that@[simps]
used are the projections from these notation classes instead of the projections of the superclasses. Example: ifhas_mul
is tagged with@[notation_class]
then the projection used forsemigroup
will beλ α hα, @has_mul.mul α (@semigroup.to_has_mul α hα)
instead of@semigroup.mul
.
Tags #
structures, projections, simp, simplifier, generates declarations
The type of rules that specify how metadata for projections in changes.
See initialize_simps_projection
.
You can specify custom projections for the @[simps]
attribute.
To do this for the projection my_structure.original_projection
by adding a declaration
my_structure.simps.my_projection
that is definitionally equal to
my_structure.original_projection
but has the projection in the desired (simp-normal) form.
Then you can call
initialize_simps_projections (original_projection → my_projection, ...)
to register this projection. See initialize_simps_projections_cmd
for more information.
You can also specify custom projections that are definitionally equal to a composite of multiple
projections. This is often desirable when extending structures (without old_structure_cmd
).
has_coe_to_fun
and notation class (like has_mul
) instances will be automatically used, if they
are definitionally equal to a projection of the structure (but not when they are equal to the
composite of multiple projections).
This command specifies custom names and custom projections for the simp attribute simps_attr
.
- You can specify custom names by writing e.g.
initialize_simps_projections equiv (to_fun → apply, inv_fun → symm_apply)
. - See Note [custom simps projection] and the examples below for information how to declare custom projections.
- If no custom projection is specified, the projection will be
coe_fn
/⇑
if ahas_coe_to_fun
instance has been declared, or the notation of a notation class (likehas_mul
) if such an instance is available. If none of these cases apply, the projection itself will be used. - You can disable a projection by default by running
initialize_simps_projections equiv (-inv_fun)
This will ensure that no simp lemmas are generated for this projection, unless this projection is explicitly specified by the user. - If you want the projection name added as a prefix in the generated lemma name, you can add the
as_prefix
modifier:initialize_simps_projections equiv (to_fun → coe as_prefix)
Note that this does not influence the parsing of projection names: if you have a declarationfoo
and you want to apply the projectionssnd
,coe
(which is a prefix) andfst
, in that order you can run@[simps snd_coe_fst] def foo ...
and this will generate a lemma with the namecoe_foo_snd_fst
.- Run
initialize_simps_projections?
(orset_option trace.simps.verbose true
) to see the generated projections.
- Run
- You can declare a new name for a projection that is the composite of multiple projections, e.g.
You can also make your custom projection that is definitionally equal to a composite of projections. In this case, coercions and notation classes are not automatically recognized, and should be manually given by giving a custom projection. This is especially useful when extending a structure (without
structure A := (proj : ℕ) structure B extends A initialize_simps_projections? B (to_A_proj → proj, -to_A)
old_structure_cmd
). In the above example, it is desirable to add-to_A
, so that@[simps]
doesn't automatically apply theB.to_A
projection and then recursively theA.proj
projection in the lemmas it generates. If you want to get both thefoo_proj
andfoo_to_A
simp lemmas, you can use@[simps, simps to_A]
. - Running
initialize_simps_projections my_struc
without arguments is not necessary, it has the same effect if you just add@[simps]
to a declaration. - If you do anything to change the default projections, make sure to call either
@[simps]
orinitialize_simps_projections
in the same file as the structure declaration. Otherwise, you might have a file that imports the structure, but not your custom projections.
Some common uses:
- If you define a new homomorphism-like structure (like
mul_hom
) you can just runinitialize_simps_projections
after defining thehas_coe_to_fun
instanceThis will generateinstance {mM : has_mul M} {mN : has_mul N} : has_coe_to_fun (M →ₙ* N) := ... initialize_simps_projections mul_hom (to_fun → apply)
foo_apply
lemmas for each declarationfoo
. - If you prefer
coe_foo
lemmas that state equalities between functions, useinitialize_simps_projections mul_hom (to_fun → coe as_prefix)
In this case you have to use@[simps {fully_applied := ff}]
or equivalently@[simps as_fn]
whenever you call@[simps]
. - You can also initialize to use both, in which case you have to choose which one to use by default,
by using either of the following
In the first case, you can get both lemmas using
initialize_simps_projections mul_hom (to_fun → apply, to_fun → coe, -coe as_prefix) initialize_simps_projections mul_hom (to_fun → apply, to_fun → coe as_prefix, -apply)
@[simps, simps coe as_fn]
and in the second case you can get both lemmas using@[simps as_fn, simps apply]
. - If your new homomorphism-like structure extends another structure (without
old_structure_cmd
) (likerel_embedding
), then you have to specify explicitly that you want to use a coercion as a custom projection. For exampledef rel_embedding.simps.apply (h : r ↪r s) : α → β := h initialize_simps_projections rel_embedding (to_embedding_to_fun → apply, -to_embedding)
- If you have an isomorphism-like structure (like
equiv
) you often want to define a custom projection for the inverse:def equiv.simps.symm_apply (e : α ≃ β) : β → α := e.symm initialize_simps_projections equiv (to_fun → apply, inv_fun → symm_apply)
- attrs : list name
- simp_rhs : bool
- type_md : tactic.transparency
- rhs_md : tactic.transparency
- fully_applied : bool
- not_recursive : list name
- trace : bool
- add_additive : option name
Configuration options for the @[simps]
attribute.
attrs
specifies the list of attributes given to the generated lemmas. Default:[`simp]
. The attributes can be either basic attributes, or user attributes without parameters. There are two attributes whichsimps
might add itself:- If
[`simp]
is in the list, then[`_refl_lemma]
is added automatically if appropriate. - If the definition is marked with
@[to_additive ...]
then all generated lemmas are marked with@[to_additive]
. This is governed by theadd_additive
configuration option.
- If
- if
simp_rhs
istt
then the right-hand-side of the generated lemmas will be put in simp-normal form. More precisely:dsimp, simp
will be called on all these expressions. See note [dsimp, simp]. type_md
specifies how aggressively definitions are unfolded in the type of expressions for the purposes of finding out whether the type is a function type. Default:instances
. This will unfold coercion instances (so that a coercion to a function type is recognized as a function type), but not declarations likeset
.rhs_md
specifies how aggressively definition in the declaration are unfolded for the purposes of finding out whether it is a constructor. Default:none
Exception:@[simps]
will automatically add the options{rhs_md := semireducible, simp_rhs := tt}
if the given definition is not a constructor with the given reducibility setting forrhs_md
.- If
fully_applied
isff
then the generatedsimp
lemmas will be between non-fully applied terms, i.e. equalities between functions. This does not restrict the recursive behavior of@[simps]
, so only the "final" projection will be non-fully applied. However, it can be used in combination with explicit field names, to get a partially applied intermediate projection. - The option
not_recursive
contains the list of names of types for which@[simps]
doesn't recursively apply projections. For example, given an equivalenceα × β ≃ β × α
one usually wants to only apply the projections forequiv
, and not also those for×
. This option is only relevant if no explicit projection names are given as argument to@[simps]
. - The option
trace
is set tott
when you write@[simps?]
. In this case, the attribute will print all generated lemmas. It is almost the same as setting the optiontrace.simps.verbose
, except that it doesn't print information about the found projections. - if
add_additive
issome nm
then@[to_additive]
is added to the generated lemma. This option is automatically set tott
when the original declaration was tagged with@[to_additive, simps]
(in that order), wherenm
is the additive name of the original declaration.
Instances for simps_cfg
- simps_cfg.has_sizeof_inst
- simps_cfg.has_reflect
- simps_cfg.inhabited
A common configuration for @[simps]
: generate equalities between functions instead equalities
between fully applied expressions.
Equations
- as_fn = {attrs := [name.mk_string "simp" name.anonymous], simp_rhs := bool.ff, type_md := tactic.transparency.instances, rhs_md := tactic.transparency.none, fully_applied := bool.ff, not_recursive := [name.mk_string "prod" name.anonymous, name.mk_string "pprod" name.anonymous], trace := bool.ff, add_additive := option.none name}
A common configuration for @[simps]
: don't tag the generated lemmas with @[simp]
.
Equations
- lemmas_only = {attrs := list.nil name, simp_rhs := bool.ff, type_md := tactic.transparency.instances, rhs_md := tactic.transparency.none, fully_applied := bool.tt, not_recursive := [name.mk_string "prod" name.anonymous, name.mk_string "pprod" name.anonymous], trace := bool.ff, add_additive := option.none name}
The @[simps]
attribute automatically derives lemmas specifying the projections of this
declaration.
Example:
@[simps] def foo : ℕ × ℤ := (1, 2)
derives two simp
lemmas:
@[simp] lemma foo_fst : foo.fst = 1
@[simp] lemma foo_snd : foo.snd = 2
-
It does not derive
simp
lemmas for the prop-valued projections. -
It will automatically reduce newly created beta-redexes, but will not unfold any definitions.
-
If the structure has a coercion to either sorts or functions, and this is defined to be one of the projections, then this coercion will be used instead of the projection.
-
If the structure is a class that has an instance to a notation class, like
has_mul
, then this notation is used instead of the corresponding projection. -
You can specify custom projections, by giving a declaration with name
{structure_name}.simps.{projection_name}
. See Note [custom simps projection].Example:
def equiv.simps.inv_fun (e : α ≃ β) : β → α := e.symm @[simps] def equiv.trans (e₁ : α ≃ β) (e₂ : β ≃ γ) : α ≃ γ := ⟨e₂ ∘ e₁, e₁.symm ∘ e₂.symm⟩
generates
@[simp] lemma equiv.trans_to_fun : ∀ {α β γ} (e₁ e₂) (a : α), ⇑(e₁.trans e₂) a = (⇑e₂ ∘ ⇑e₁) a @[simp] lemma equiv.trans_inv_fun : ∀ {α β γ} (e₁ e₂) (a : γ), ⇑((e₁.trans e₂).symm) a = (⇑(e₁.symm) ∘ ⇑(e₂.symm)) a
-
You can specify custom projection names, by specifying the new projection names using
initialize_simps_projections
. Example:initialize_simps_projections equiv (to_fun → apply, inv_fun → symm_apply)
. Seeinitialize_simps_projections_cmd
for more information. -
If one of the fields itself is a structure, this command will recursively create
simp
lemmas for all fields in that structure.- Exception: by default it will not recursively create
simp
lemmas for fields in the structuresprod
andpprod
. You can give explicit projection names or change the value ofsimps_cfg.not_recursive
to override this behavior.
Example:
structure my_prod (α β : Type*) := (fst : α) (snd : β) @[simps] def foo : prod ℕ ℕ × my_prod ℕ ℕ := ⟨⟨1, 2⟩, 3, 4⟩
generates
@[simp] lemma foo_fst : foo.fst = (1, 2) @[simp] lemma foo_snd_fst : foo.snd.fst = 3 @[simp] lemma foo_snd_snd : foo.snd.snd = 4
- Exception: by default it will not recursively create
-
You can use
@[simps proj1 proj2 ...]
to only generate the projection lemmas for the specified projections. -
Recursive projection names can be specified using
proj1_proj2_proj3
. This will create a lemma of the formfoo.proj1.proj2.proj3 = ...
.Example:
structure my_prod (α β : Type*) := (fst : α) (snd : β) @[simps fst fst_fst snd] def foo : prod ℕ ℕ × my_prod ℕ ℕ := ⟨⟨1, 2⟩, 3, 4⟩
generates
@[simp] lemma foo_fst : foo.fst = (1, 2) @[simp] lemma foo_fst_fst : foo.fst.fst = 1 @[simp] lemma foo_snd : foo.snd = {fst := 3, snd := 4}
-
If one of the values is an eta-expanded structure, we will eta-reduce this structure.
Example:
structure equiv_plus_data (α β) extends α ≃ β := (data : bool) @[simps] def bar {α} : equiv_plus_data α α := { data := tt, ..equiv.refl α }
generates the following:
@[simp] lemma bar_to_equiv : ∀ {α : Sort*}, bar.to_equiv = equiv.refl α @[simp] lemma bar_data : ∀ {α : Sort*}, bar.data = tt
This is true, even though Lean inserts an eta-expanded version of
equiv.refl α
in the definition ofbar
. -
For configuration options, see the doc string of
simps_cfg
. -
The precise syntax is
('simps' ident* e)
, wheree
is an expression of typesimps_cfg
. -
@[simps]
reduces let-expressions where necessary. -
When option
trace.simps.verbose
is true,simps
will print the projections it finds and the lemmas it generates. The same can be achieved by using@[simps?]
, except that in this case it will not print projection information. -
Use
@[to_additive, simps]
to apply bothto_additive
andsimps
to a definition, making sure thatsimps
comes afterto_additive
. This will also generate the additive versions of allsimp
lemmas.