Lean 4.6.0 (2024-02-29)
-
Add custom simplification procedures (aka
simproc
s) tosimp
. Simprocs can be triggered by the simplifier on a specified term-pattern. Here is an small example:import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat def foo (x : Nat) : Nat := x + 10 /-- The `simproc` `reduceFoo` is invoked on terms that match the pattern `foo _`. -/ simproc reduceFoo (foo _) := /- A term of type `Expr β SimpM Step -/ fun e => do /- The `Step` type has three constructors: `.done`, `.visit`, `.continue`. * The constructor `.done` instructs `simp` that the result does not need to be simplified further. * The constructor `.visit` instructs `simp` to visit the resulting expression. * The constructor `.continue` instructs `simp` to try other simplification procedures. All three constructors take a `Result`. The `.continue` constructor may also take `none`. `Result` has two fields `expr` (the new expression), and `proof?` (an optional proof). If the new expression is definitionally equal to the input one, then `proof?` can be omitted or set to `none`. -/ /- `simp` uses matching modulo reducibility. So, we ensure the term is a `foo`-application. -/ unless e.isAppOfArity ``foo 1 do return .continue /- `Nat.fromExpr?` tries to convert an expression into a `Nat` value -/ let some n β Nat.fromExpr? e.appArg! | return .continue return .done { expr := Lean.mkNatLit (n+10) }
We disable simprocs support by using the command
set_option simprocs false
. This command is particularly useful when porting files to v4.6.0. Simprocs can be scoped, manually added tosimp
commands, and suppressed using-
. They are also supported bysimp?
.simp only
does not execute anysimproc
. Here are some examples for thesimproc
defined above.example : x + foo 2 = 12 + x := by set_option simprocs false in /- This `simp` command does not make progress since `simproc`s are disabled. -/ fail_if_success simp simp_arith example : x + foo 2 = 12 + x := by /- `simp only` must not use the default simproc set. -/ fail_if_success simp only simp_arith example : x + foo 2 = 12 + x := by /- `simp only` does not use the default simproc set, but we can provide simprocs as arguments. -/ simp only [reduceFoo] simp_arith example : x + foo 2 = 12 + x := by /- We can use `-` to disable `simproc`s. -/ fail_if_success simp [-reduceFoo] simp_arith
The command
register_simp_attr <id>
now creates asimp
and asimproc
set with the name<id>
. The following command instructs Lean to insert thereduceFoo
simplification procedure into the setmy_simp
. If no set is specified, Lean uses the defaultsimp
set.simproc [my_simp] reduceFoo (foo _) := ...
-
The syntax of the
termination_by
anddecreasing_by
termination hints is overhauled:-
They are now placed directly after the function they apply to, instead of after the whole
mutual
block. -
Therefore, the function name no longer has to be mentioned in the hint.
-
If the function has a
where
clause, thetermination_by
anddecreasing_by
for that function come before thewhere
. The functions in thewhere
clause can have their own termination hints, each following the corresponding definition. -
The
termination_by
clause can only bind βextra parametersβ, that are not already bound by the function header, but are bound in a lambda (:= fun x y z =>
) or in patterns (| x, n + 1 => β¦
). These extra parameters used to be understood as a suffix of the function parameters; now it is a prefix.
Migration guide: In simple cases just remove the function name, and any variables already bound at the header.
def foo : Nat β Nat β Nat := β¦ -termination_by foo a b => a - b +termination_by a b => a - b
or
def foo : Nat β Nat β Nat := β¦ -termination_by _ a b => a - b +termination_by a b => a - b
If the parameters are bound in the function header (before the
:
), remove them as well:def foo (a b : Nat) : Nat := β¦ -termination_by foo a b => a - b +termination_by a - b
Else, if there are multiple extra parameters, make sure to refer to the right ones; the bound variables are interpreted from left to right, no longer from right to left:
def foo : Nat β Nat β Nat β Nat | a, b, c => β¦ -termination_by foo b c => b +termination_by a b => b
In the case of a
mutual
block, place the termination arguments (without the function name) next to the function definition:-mutual -def foo : Nat β Nat β Nat := β¦ -def bar : Nat β Nat := β¦ -end -termination_by - foo a b => a - b - bar a => a +mutual +def foo : Nat β Nat β Nat := β¦ +termination_by a b => a - b +def bar : Nat β Nat := β¦ +termination_by a => a +end
Similarly, if you have (mutual) recursion through
where
orlet rec
, the termination hints are now placed directly after the function they apply to:-def foo (a b : Nat) : Nat := β¦ - where bar (x : Nat) : Nat := β¦ -termination_by - foo a b => a - b - bar x => x +def foo (a b : Nat) : Nat := β¦ +termination_by a - b + where + bar (x : Nat) : Nat := β¦ + termination_by x -def foo (a b : Nat) : Nat := - let rec bar (x : Nat) : Nat := β¦ - β¦ -termination_by - foo a b => a - b - bar x => x +def foo (a b : Nat) : Nat := + let rec bar (x : Nat) : Nat := β¦ + termination_by x + β¦ +termination_by a - b
In cases where a single
decreasing_by
clause applied to multiple mutually recursive functions before, the tactic now has to be duplicated. -
-
The semantics of
decreasing_by
changed; the tactic is applied to all termination proof goals together, not individually.This helps when writing termination proofs interactively, as one can focus each subgoal individually, for example using
Β·
. Previously, the given tactic script had to work for all goals, and one had to resort to tactic combinators likefirst
:def foo (n : Nat) := β¦ foo e1 β¦ foo e2 β¦ -decreasing_by -simp_wf -first | apply something_about_e1; β¦ - | apply something_about_e2; β¦ +decreasing_by +all_goals simp_wf +Β· apply something_about_e1; β¦ +Β· apply something_about_e2; β¦
To obtain the old behaviour of applying a tactic to each goal individually, use
all_goals
:def foo (n : Nat) := β¦ -decreasing_by some_tactic +decreasing_by all_goals some_tactic
In the case of mutual recursion each
decreasing_by
now applies to just its function. If some functions in a recursive group do not have their owndecreasing_by
, the defaultdecreasing_tactic
is used. If the same tactic ought to be applied to multiple functions, thedecreasing_by
clause has to be repeated at each of these functions. -
Modify
InfoTree.context
to facilitate augmenting it with partial contexts while elaborating a command. This breaks backwards compatibility with all downstream projects that traverse theInfoTree
manually instead of going through the functions inInfoUtils.lean
, as well as those manually creating and savingInfoTree
s. See PR #3159 for how to migrate your code. -
Add language server support for call hierarchy requests (PR #3082). The change to the .ilean format in this PR means that projects must be fully rebuilt once in order to generate .ilean files with the new format before features like "find references" work correctly again.
-
Structure instances with multiple sources (for example
{a, b, c with x := 0}
) now have their fields filled from these sources in strict left-to-right order. Furthermore, the structure instance elaborator now aggressively use sources to fill in subobject fields, which prevents unnecessary eta expansion of the sources, and hence greatly reduces the reliance on costly structure eta reduction. This has a large impact on mathlib, reducing total CPU instructions by 3% and enabling impactful refactors like leanprover-community/mathlib4#8386 which reduces the build time by almost 20%. See PR #2478 and RFC #2451. -
Add pretty printer settings to omit deeply nested terms (
pp.deepTerms false
andpp.deepTerms.threshold
) (PR #3201) -
Add pretty printer options
pp.numeralTypes
andpp.natLit
. Whenpp.numeralTypes
is true, then natural number literals, integer literals, and rational number literals are pretty printed with type ascriptions, such as(2 : Rat)
,(-2 : Rat)
, and(-2 / 3 : Rat)
. Whenpp.natLit
is true, then raw natural number literals are pretty printed asnat_lit 2
. PR #2933 and RFC #3021.
Lake updates:
Other improvements:
-
make
intro
be aware oflet_fun
#3115 -
produce simpler proof terms in
rw
#3121 -
fuse nested
mkCongrArg
calls in proofs generated bysimp
#3203 -
induction using
followed by a general term #3188 -
reducing out-of-bounds
swap!
should returna
, not `default`` #3197, fixing #3196 -
derive
BEq
on structure withProp
-fields #3191, fixing #3140 -
refine through more
casesOnApp
/matcherApp
#3176, fixing #3175 -
do not strip dotted components from lean module names #2994, fixing #2999
-
fix
deriving
only deriving the first declaration for some handlers #3058, fixing #3057 -
do not instantiate metavariables in kabstract/rw for disallowed occurrences #2539, fixing #2538
-
hover info for
cases h : ...
#3084