Zulip Chat Archive
Stream: lean4
Topic: Sums in recursively defined functions on the natural numbers
Thomas Preu (Aug 10 2025 at 11:18):
I am trying to understand the faculties and limitations of Lean in defining functions on the natural numbers recursively. Is it possible to sum over values of all smaller arguments in defining the function?
Here are some naive attempts with a toy example as an MWE. They yield an error "fail to show termination".
import Mathlib.Tactic.Ring
import Mathlib.Data.Finset.Basic
import Mathlib.Algebra.BigOperators.Fin
#eval ∑ i in Finset.range 5, i
--does not work - Lean4 cannot establish termination of recursion ahead.
def frec1 : Nat → Nat
| Nat.zero => 1
| Nat.succ n => ∑ i in Finset.range (Nat.succ n), (frec1 i)
#eval frec1 5
--does not work - Lean4 cannot establish termination of recursion ahead.
def frec2 : Nat → Nat
| Nat.zero => 1
| Nat.succ n => (List.range (Nat.succ n)).map frec2 |>.foldl (· + ·) 0
#eval frec2 5
def frec3 : Nat → Nat
| Nat.zero => 1
| Nat.succ n => frec3 n + frec3 n
--work around: first frec3 n means ∑ᵢ<ₙ (frec3 i) and second frec3 n stands for itself.
--this requires some rewriting on the part of the user, unsatisfactory.
#eval frec3 5
Does anyone know how to achieve this without resorting to work arounds as indicated above?
Remark: I have Lean 4.5 installed, which might be outdated and the libraries might have changed names.
Robin Arnez (Aug 10 2025 at 11:29):
You can use:
def frec : Nat → Nat
| 0 => 1
| n => ∑ i : Fin n, frec i
Kenny Lau (Aug 10 2025 at 11:32):
@Thomas Preu this is because when you have i in Finset.range (Nat.succ n), in the context of the application frec1 i, Lean only knows that i is a natural number
Kenny Lau (Aug 10 2025 at 11:33):
summing over Fin n as instructed above allows you to carry the proof of i < n
Kenny Lau (Aug 10 2025 at 11:33):
Finset.range (Nat.succ n) returns a term of Finset (Nat), and the summation syntax sums over a function on Nat when you have Finset Nat
Thomas Preu (Aug 10 2025 at 13:32):
Thanks for the help and the explanation, now it works.
Thomas Preu (Aug 10 2025 at 13:44):
@Kenny Lau Interestingly enough the standalone sum does not work like this.
-- works
#eval ∑ i in Finset.range 5, i
-- yields error "failed to synthesize AddCommMonoid (Fin 5)"
#eval ∑ i : Fin 5, i
Do you have an explanation for that?
Kenny Lau (Aug 10 2025 at 13:45):
@Thomas Preu if you just type i it interprets it as a term of Fin 5, which has no addition defined
Kenny Lau (Aug 10 2025 at 13:45):
but in the code above frec forces it to be coerced to Nat
Thomas Preu (Aug 10 2025 at 13:51):
Now it works:
#eval ∑ i : Fin 5, (↑i : Nat)
Kenny Lau (Aug 10 2025 at 13:51):
lean tip: basically don't ever type the uparrow, it only confuses you
Thomas Preu (Aug 10 2025 at 13:54):
OK, so this works better:
#eval ∑ i : Fin 5, (i : Nat)
Ruben Van de Velde (Aug 10 2025 at 14:42):
Kenny Lau said:
lean tip: basically don't ever type the uparrow, it only confuses you
I don't agree with this tip - sometimes you need to specify exactly where the coercion goes, e.g. (<arrow>(a+b) : Int)
Kenny Lau (Aug 10 2025 at 14:42):
but that isn't simp NF
Kenny Lau (Aug 10 2025 at 14:43):
i think it's better to drill into people the idea that the simp NF is ↑a + ↑b not ↑(a+b)
Ruben Van de Velde (Aug 10 2025 at 14:47):
I don't see how your comment is relevant
Kenny Lau (Aug 10 2025 at 14:47):
if we tell people that the idiomatic way is (term : expected type), then they will type (a + b : Int) and see ↑a + ↑b, which is the correct simpNF
Chris Henson (Aug 10 2025 at 16:46):
Kenny Lau said:
if we tell people that the idiomatic way is (term : expected type), then they will type (a + b : Int) and see ↑a + ↑b, which is the correct simpNF
In general, is this considered the preferred way for users to discover simp normal forms? Is there documentation that recommends this?
Kenny Lau (Aug 10 2025 at 16:47):
well all of the map_add lemmas look like f(a+b) = f a + f b
Thomas Preu (Aug 12 2025 at 08:48):
What I, as a beginner, take from this:
- The ↑ specifies the exact subterm that is supposed to be coerced.
- Lean output features them by default if coercions occur.
- Usually when coercions are not automatically inferred by Lean, type annotation like
(... : type)is sufficient, ↑ is not necessary, ↑ is considered obfuscating to human readers and should therefore be omitted. - Sometimes however, type annotation is not enough and then an additional ↑ clarifies the place of coercion.
Did I get this right from your discussion? Did I miss something?
Kenny Lau (Aug 12 2025 at 08:54):
that's what i think anyway
Aaron Liu (Aug 12 2025 at 10:37):
One more thing - the default coercion is "no coercion", so if you don't specify what to coerce to then even if you put a ↑ if you haven't disallowed "no coercion" as an option then that's what will happen.
Robin Arnez (Aug 26 2025 at 16:45):
I prefer something like ((a + b : Nat) : Int) for this kind of coercion -- it's a bit clearer and the elaborator can't really do anything wrong
Last updated: Dec 20 2025 at 21:32 UTC