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