lean4-htt/tests/elab/structuralNamedF.lean
Joachim Breitner 26ad4d6972
feat: name the functional argument to brecOn in structural recursion (#12987)
This PR extracts the functional (lambda) passed to `brecOn` in
structural
recursion into a named `_f` helper definition (e.g. `foo._f`), similar
to
how well-founded recursion uses `._unary`. This way the functional shows
up
with a helpful name in kernel diagnostics rather than as an anonymous
lambda.

The `_f` definition is added with `.abbrev` kernel reducibility hints
and
the `@[reducible]` elaborator attribute, so the kernel unfolds it
eagerly
after `brecOn` iota-reduces. For inductive predicates, the previous
inline
lambda behavior is kept.

To ensure that parent definitions still get the correct reducibility
height
(since `getMaxHeight` ignores `.abbrev` definitions), each `_f`'s body
height is registered via a new `defHeightOverrideExt` environment
extension.
`getMaxHeight` checks this extension for all definitions, making the
height
computation transparent to the extraction.

This change improves code size (a bit). It may regress kernel reduction
times,
especially if a function defined by structural recursion is used in
kernel reduction
proofs on the hot path. Functions defined by structural recursion are
not particularly
fast to reduce anyways (due to the `.brecOn` construction), so already
now it may be
worth writing a kernel-reduction-friendly function manually (using the
recursor directly,
avoiding overloaded operations). This change will guide you in knowing
which function to
optimize.


🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:40:18 +00:00

77 lines
2.2 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- Test that structural recursion creates a named `_f` helper definition
-- for the functional passed to `brecOn`.
-- Simple case: single function
def addAdjacent : List Nat → List Nat
| [] => []
| [a] => [a]
| a::b::as => (a+b) :: addAdjacent as
-- The `_f` helper should exist in the environment
/-- info: addAdjacent._f : (x : List Nat) → List.below x → List Nat -/
#guard_msgs in #check @addAdjacent._f
-- Verify computation still works
/-- info: [3, 7] -/
#guard_msgs in #eval addAdjacent [1, 2, 3, 4]
-- Mutual recursion: each function gets its own `_f`
mutual
def even : Nat → Bool
| 0 => true
| n + 1 => odd n
def odd : Nat → Bool
| 0 => false
| n + 1 => even n
end
/-- info: even._f : (x : Nat) → Nat.below x → Bool -/
#guard_msgs in #check @even._f
/-- info: odd._f : (x : Nat) → Nat.below x → Bool -/
#guard_msgs in #check @odd._f
/-- info: true -/
#guard_msgs in #eval even 4
/-- info: true -/
#guard_msgs in #eval odd 3
-- With fixed parameters
def myMap (f : α → β) : List α → List β
| [] => []
| x::xs => f x :: myMap f xs
/-- info: @myMap._f : {α : Type u_1} → {β : Type u_2} → (α → β) → (x : List α) → List.below x → List β -/
#guard_msgs in #check @myMap._f
-- The `_f` helper shows up with a helpful name in kernel diagnostics
def fib (n : Nat) :=
match n with
| 0 | 1 => 1
| x+2 => fib x + fib (x+1)
termination_by structural n
/--
trace: [diag] Diagnostics
[reduction] unfolded declarations (max: 79, num: 4):
[reduction] Nat.rec ↦ 79
[reduction] Add.add ↦ 41
[reduction] HAdd.hAdd ↦ 41
[reduction] fib ↦ 40
[reduction] unfolded reducible declarations (max: 79, num: 1):
[reduction] Nat.casesOn ↦ 79
[kernel] unfolded declarations (max: 80, num: 6):
[kernel] Nat.rec ↦ 80
[kernel] Nat.casesOn ↦ 77
[kernel] fib._f ↦ 39
[kernel] fib.match_1 ↦ 39
[kernel] Add.add ↦ 36
[kernel] HAdd.hAdd ↦ 36
use `set_option diagnostics.threshold <num>` to control threshold for reporting counters
-/
#guard_msgs in
set_option diagnostics true in
set_option diagnostics.threshold 10 in
theorem fib_20 : fib 20 = 10946 := rfl