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_attrandsimps_cfgfor 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_mulandhas_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_mulis tagged with@[notation_class]then the projection used forsemigroupwill 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_funinstance 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_prefixmodifier:initialize_simps_projections equiv (to_fun → coe as_prefix)Note that this does not influence the parsing of projection names: if you have a declarationfooand 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_Aprojection and then recursively theA.projprojection in the lemmas it generates. If you want to get both thefoo_projandfoo_to_Asimp lemmas, you can use@[simps, simps to_A]. - Running
initialize_simps_projections my_strucwithout 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_projectionsin 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_projectionsafter defining thehas_coe_to_funinstanceThis 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_applylemmas for each declarationfoo. - If you prefer
coe_foolemmas 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.
attrsspecifies 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 whichsimpsmight 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_additiveconfiguration option.
- If
- if
simp_rhsisttthen the right-hand-side of the generated lemmas will be put in simp-normal form. More precisely:dsimp, simpwill be called on all these expressions. See note [dsimp, simp]. type_mdspecifies 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_mdspecifies how aggressively definition in the declaration are unfolded for the purposes of finding out whether it is a constructor. Default:noneException:@[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_appliedisffthen the generatedsimplemmas 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_recursivecontains 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
traceis set tottwhen 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_additiveissome nmthen@[to_additive]is added to the generated lemma. This option is automatically set tottwhen the original declaration was tagged with@[to_additive, simps](in that order), wherenmis 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
simplemmas 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_cmdfor more information. -
If one of the fields itself is a structure, this command will recursively create
simplemmas for all fields in that structure.- Exception: by default it will not recursively create
simplemmas for fields in the structuresprodandpprod. You can give explicit projection names or change the value ofsimps_cfg.not_recursiveto 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 = ttThis 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), whereeis an expression of typesimps_cfg. -
@[simps]reduces let-expressions where necessary. -
When option
trace.simps.verboseis true,simpswill 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_additiveandsimpsto a definition, making sure thatsimpscomes afterto_additive. This will also generate the additive versions of allsimplemmas.