Zulip Chat Archive
Stream: batteries
Topic: simpNF lint with optional args
cmlsharp (Jan 08 2026 at 19:23):
My scan PR for arrays is currently failing CI because simpNF wants me to specify the optional arguments (start and stop indices) on the LHS of every theorem.
so e.g.
theorem scanl_reverse {f : β → α → β} {as : Array α} :
scanl f init as.reverse = reverse (scanr (flip f) init as) := by
would need to be written as
theorem scanl_reverse {f : β → α → β} {as : Array α} :
scanl f init as.reverse 0 as.size = reverse (scanr (flip f) init as) := by
To me it seems like this clutters up the theorem definitions. Should I make this change to every theorem, or if not is it possible to disable this lint?
cmlsharp (Jan 08 2026 at 19:24):
(the fact that the array versions have start/stop indices is just for parity with Array.foldl/foldr and so Subarray.scan* can be implemented in terms of the array version.
Jovan Gerbscheid (Jan 12 2026 at 10:07):
It's a bit hard to help if you don't share the error message of simpNF
cmlsharp (Jan 12 2026 at 14:41):
.../Batteries/Data/Array/Scan.lean:355:1: error: @Array.scanl_reverse Left-hand side simplifies from
Array.scanl f init as.reverse
to
Array.scanl f init as.reverse 0 as.size
using
simp only [*, @Array.size_reverse]
Try to change the left-hand side to the simplified term!
cmlsharp (Jan 12 2026 at 14:47):
I suppose the issue here is that the default argument ends up becoming 'as.reverse.size', which simpNF wants to simplify to as.size. That's fair enough I suppose but it requires me to specify the default argument in the theorem statement.
Analogous theorems for Array.foldl in the standard library do not infect the theorem statement like this
cmlsharp (Jan 12 2026 at 14:48):
(just for reference this is the type of Array.scanl
def scanl (f : β → α → β) (init : β) (as : Array α) (start := 0) (stop := as.size) : Array β
Jovan Gerbscheid (Jan 12 2026 at 14:50):
Exactly, and the simpNF linter has a good point: if you have the LHS with as.reverse.size, then this simp lemma is useless. Those theorems in the standard library are also useless simp theorems.
Jovan Gerbscheid (Jan 12 2026 at 14:59):
Sorry I was wrong, in the standard library they do address this issue correctly. They have the following theorem for simp:
/-- Variant of `foldl_reverse` with a side condition for the `stop` argument. -/
@[simp] theorem foldl_reverse' {xs : Array α} {f : β → α → β} {b} {stop : Nat}
(w : stop = xs.size) :
xs.reverse.foldl f b 0 stop = xs.foldr (fun x y => f y x) b :=
cmlsharp (Jan 12 2026 at 15:02):
ah I see, so the righgt way to handle such cases is to provide two versions of the lemma, one "main" version without optional args specified or a simp annotation and then an equivalent lemma with the simp annotation and the optional arguments specified (and simplified)?
Jovan Gerbscheid (Jan 12 2026 at 15:10):
Indeed. Their Array.foldl_reverse' version has an extra stop variable with a hypothesis stop = xs.size. I presume that this comes in handy if xs is e.g. a literal array, something like #[x,y,z], in which case the simplified form of #[x,y,z].reverse.size is 3. If the lemma had xs.size inlined in xs.reverse.foldl f b 0 xs.size, then unification would fail to unify #[x,y,z].size with 3.
Last updated: Feb 28 2026 at 14:05 UTC