leanprover / leanprover/lean4

RFC: Array operations: Avoid duplicating arguments with (stop := as.size)

Open
#2,981 14 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

P-medium RFC
Dominant language
Lean
Stars
9.2k
Forks
990
Avg merge
1d 17h
Merged PRs (30d)
175

Description

The issue

I have just stumbed over the fact that many Array operations (foldl etc.) have a parameter
stop with a default argument (stop := as.size), and this duplicates the array argument, which may be large:

def foo : Bool := Array.any #[1,2,3,4] (fun _ => true)
#print foo

prints

def foo : Bool :=
Array.any #[1, 2, 3, 4] (fun x => true) 0 (Array.size #[1, 2, 3, 4])

This duplication also shows up in other places, e.g. in well-founded recursive definitions (which, unsurprisingly, is how I noticed this) a recursive call in the array will cause the tactic to be invoked multiple times:

def rec : Nat → Bool
 | 0 => true
 | n+1 =>  Array.any #[rec n, rec n, rec n, rec n] id
decreasing_by trace "Tactic is run"; decreasing_tactic -- called 8 times

Worse, nesting these innocent looking funcitons now lead to a combinatoric explosion:

def rec2 : Nat → Bool
 | 0 => true
 | n+1 => 
    Array.any #[
      Array.any #[rec2 n, rec2 n, rec2 n, rec2 n] id,
      Array.any #[rec2 n, rec2 n, rec2 n, rec2 n] id,
      Array.any #[rec2 n, rec2 n, rec2 n, rec2 n] id,
      Array.any #[rec2 n, rec2 n, rec2 n, rec2 n] id
    ] id
decreasing_by trace "Tactic is run"; decreasing_tactic -- called 64 times
Proposal

A possible fix is to update all these functions as follows:

Change

unsafe def foldlMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Array α) (start := 0) (stop := as.size) : m β :=

to

unsafe def foldlMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Array α) (start := 0) (stop := Option as.size) : m β :=
  let stop := stop.getD as.size

Many of the ~19 affected functions just pass stop on, so they only change the type, not the implementation.

Alternative

The only alternative I see is to change how optional arguments are elaborated, and if they mention earlier parameters, that parameter is let-bound instead of duplicated.

I do not know if there are users who actually rely on default paramters being able to refer not just to the value of an earlier argument, but actually need the argument syntax to be replicated.

Impact

Add 👍 to issues you consider important. If others benefit from the changes in this proposal being added, please ask them to add 👍 to it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the shown foldlMUnsafe signature, Array.any, and the other Array operations that pass stop through. Compare the proposed Option-based defaults with the alternative elaboration change, then verify that generated terms no longer duplicate earlier array arguments while existing optional-argument behavior remains supported.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.