Skip to content

Commit f4ee72b

Browse files
authored
feat: minimal iterator library (#8358)
This PR introduces a very minimal version of the new iterator library. It comes with list iterators and various consumers, namely `toArray`, `toList`, `toListRev`, `ForIn`, `fold`, `foldM` and `drain`. All consumers also come in a partial variant that can be used without any proofs. This limited version of the iterator library generates decent code, even with the old code generator.
1 parent 8535a22 commit f4ee72b

File tree

18 files changed

+1655
-0
lines changed

18 files changed

+1655
-0
lines changed

src/Std/Data.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ import Std.Data.HashSet.RawLemmas
2323
import Std.Data.DTreeMap.Raw
2424
import Std.Data.TreeMap.Raw
2525
import Std.Data.TreeSet.Raw
26+
27+
import Std.Data.Iterators

src/Std/Data/Iterators.lean

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/-
2+
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Paul Reichert
5+
-/
6+
prelude
7+
import Std.Data.Iterators.Basic
8+
import Std.Data.Iterators.Producers
9+
import Std.Data.Iterators.Consumers
10+
import Std.Data.Iterators.Internal
11+
12+
/-!
13+
# Iterators
14+
15+
The `Std.Data.Iterators` module provides a uniform abstraction over data that can be iterated over
16+
in a sequential way, with a focus on convenience and efficiency. It is heavily inspired by Rust's
17+
iterator library and Haskell's `streamly`.
18+
19+
An iterator is an abstraction of a sequence of values that may or may not terminate. For example,
20+
every list can be traversed with an iterator via `List.iter`.
21+
22+
Most users of the iterator API will just put together existing library functions that
23+
create, combine and consume iterators. Consider a simple example:
24+
25+
```lean
26+
-- [1, 2, 3].iter : Iter (α := ListIterator α) Nat (α being implicit)
27+
#check [1, 2, 3].iter
28+
29+
-- 12
30+
#eval [1, 2, 3].iter.map (· * 2) |>.fold (init := 0) (· + ·)
31+
```
32+
33+
An iterator that emits values in `β` is an element of the type `Iter (α := ?) β`. The implicit
34+
type argument `α` contains stateful information about the iterator. `IterM (α := ?) m β` represents
35+
iterators over a monad `m`. In both cases, the implementation is provided by a typeclass
36+
`Iterator α m β`, where `m` is a monad in which the iteration happens.
37+
38+
The heart of an iterator `it : Iter β` is its `it.step` function, which returns `it.Step α β`.
39+
Here, `it.Step` is a type that either (1) provides an output value in `β` and a
40+
successor iterator (`yield`), (2) provides only a successor iterator with no output (`skip`), or
41+
(3) signals that the iterator has finished and will provide no more outputs (`done`).
42+
For technical reasons related to termination proofs, the returned `it.Step` also contains proof
43+
that it is a "plausible" step obtained from `it`.
44+
45+
The `step` function can also be used by hand:
46+
47+
```lean
48+
def sumrec (l : List Nat) : Nat :=
49+
go (l.iter.map (2 * ·)) 0
50+
where
51+
go it acc :=
52+
match it.step with
53+
| .yield it' n _ => go it' (acc + n)
54+
| .skip it' _ => go it' acc
55+
| .done _ => acc
56+
termination_by it.finitelyManySteps
57+
```
58+
59+
In general, iterators do not need to terminate after finitely many steps. This example
60+
works because the iterator type at hand has an instance of the `Std.Iterators.Finite` typeclass.
61+
Iterators that may not terminate but will not end up in an infinite sequence of `.skip`
62+
steps are called productive. This behavior is encoded in the `Std.Iterators.Productive` typeclass.
63+
64+
## Stability
65+
66+
The API for the usage of iterators provided in this module can be considered stable, as well as
67+
the API for the verification of programms using iterators, unless explicitly stated otherwise.
68+
69+
Contrarily, the API for implementation of new iterators, including the design of the `Iterator`
70+
typeclass, is still experimental and will change in the future. It is already planned that there
71+
will be a breaking change to make the iterators more flexible with regard to universes, a change
72+
that needs to wait for a language feature.
73+
74+
## Module structure
75+
76+
A distinction that cuts through the whole module is that between pure and monadic
77+
iterators. Each submodule contains a dedicated `Monadic` sub-submodule.
78+
79+
All of the following module names are prefixed with `Std.Data.Iterators`.
80+
81+
### Basic iterator API
82+
83+
* `Basic` defines `Iter` and `IterM` as well as `Iterator`, `Finite`
84+
and `Productive` typeclasses.
85+
* `Producers` provides iterator implementations for common data types.
86+
* `Consumers` provides functions that take one or more iterators, iterate over them and potentially
87+
return some result. Examples are the `toList` function and an instance for the `ForIn` typeclass.
88+
These operations allow for what is also known as *internal iteration*, where the caller delegates
89+
the control flow during the iteration to the called consumer.
90+
* `Combinators` will provide operations that transform one or more iterators into a new iterator
91+
as soon as the first such combinator has been implemented.
92+
93+
### Verification API
94+
95+
`Lemmas` will provide the means to verify programs that use iterators.
96+
97+
### Implementation details
98+
99+
`Internal` contains code that should not be relied upon because it may change in the future.
100+
This whole module is explicitly experimental and it is not advisable for downstream users to
101+
expect stability to implement their own iterators at this point in time.
102+
103+
-/

0 commit comments

Comments
 (0)