-
Notifications
You must be signed in to change notification settings - Fork 217
Delta Cheatsheet
This document aims to be a complete reference for when definitions do and do not get unfolded in Lean. It will take several weeks until it even begins to resemble such a document.
-
A definition can be unfolded eagerly during
is_def_eq
.Example:
a =?= b
could in principle unfold all[reducible]
layers of botha
andb
eagerly before proceeding. This is not currently done. Use-case:id (xs ++ ys) =?= id (id (xs ++ ys))
might just always unfold all theid
s before proceeding, regardless of definitional depth or any other heuristic. -
A definition can be unfolded lazily during
is_def_eq
.Example: if
g := f
, thenf a =?= g a
could detect that the depth ofg
is larger than the depth off
and so unfoldg
first before proceeding. Use-case:f1 (f2 ... (fN x)...) =?= f2 (f3 ... (fN x))
would only need to unfoldf1
to confirm that the two are definitionally equal. This idiom is ubiquitous with type-class instances. -
A definition can be unfolded because a Pi type or a Sort is expected or required.
Example: we may need to extract the universe level of a Sort in the app-builder.
-
A definition can be unfolded if it matches a particular pattern.
Example: we might have reflexivity-simplification rules for definitions that wrap recursors that only trigger if it is known which pattern in the definition matches.
-
A definition can be unfolded because of a unification hint.
Example: In
f =?= g
, it might be the case that neitherf
norg
would normally be unfolded but that the pair(f, g)
triggers a unification hint that causes one or both of them to be unfolded. Use-case: we wantn + 1 =?= succ n
to succeed even if+
would never otherwise get reduced. -
A definition can be unfolded during normalization if it is fully applied.
Example:
compose
has the[unfold_full]
tag, and is unfolded during normalization if it is applied to all 3 arguments (i.e.compose f g x
normalizes tof (g x)
). Note that the number of arguments is not well defined in general and depends on the delta/normalization strategy being used. Also note that we can simulate this behaviour easily with a single reflexivity-simplification rule. -
A definition can be unfolded during normalization if a specified argument is a constructor.
Example:
pred : nat -> nat
has the[unfold 1]
tag and gets unfolded during normalization if its argument is zero or succ. -
A definition can be unfolded during normalization if it is if an outer application is expecting a constructor at that position.
Example:
pair
has the[constructor]
tag and so gets unfolded during normalization if an application ofpair
occurs in the major premise ofpair.rec
. -
A definition can be unfolded eagerly and permanently by blast.
Example: blast currently unfolds every
[reducible]
definition appearing in the context during initialization. Most blast modules that track lemmas in the environment (e.g. the simplifier) also unfold[reducible]
definitions eagerly in the lemmas that they track. -
A definition can be unfolded "monotonically" by blast.
Example: given
H : f x
andf x := g x x
, blast might add the hypothesesH' : g x x := H
andHH' : H = H' := rfl
. We call this monotonic because we have not lost the original folded version. Note that we can use reflexivity-simplification rules monotonically as well.
-
Some data structures may be indexed by constant names or head symbols, and are hence inherently not robust to delta reduction.
Example: if
f := g
butf
is not eagerly reduced by blast, then a[backward]
lemmaH
with resultg
would not be considered for a backwards action for goalf
even ifis_def_eq(f, g)
. Note thatH
would not be tried even iff
were monotonically unfolded so that the context containedHfg : f = g
. However,H
would fire if e.g. asubst
action replaced the goal withg
.
-
Sometimes we may expect
is_def_eq
to fail and want to prevent a lot of wasted unfolding.Example: if we are doing search
-
Sometimes the user may say "I am willing to suffer a long delay if it turns out I am wrong about
_ =?= _
being true"Example:
exact
tactic -
Sometimes we know something is going to work, but we don't know what.
Example: even though we know something is going to work, the
assumption
tactic might still not want to unfold aggressively inH1 : P (fact 100), H2 : P 0 |- P 0
, or else risk wasting time computingfact 100
. I thinkassumption
should probably be conservative and the user should useexact
if reductions are needed, orblast
if the hypothesis in question contains system-generated names.