Skip to content

Commit 383c0ca

Browse files
authored
feat: remove Finite conditions from iterator consumers relying on a new fixpoint combinator (#11038)
This PR introduces a new fixpoint combinator, `WellFounded.extrinsicFix`. A termination proof, if provided at all, can be given extrinsically, i.e., looking at the term from the outside, and is only required if one intends to formally verify the behavior of the fixpoint. The new combinator is then applied to the iterator API. Consumers such as `toList` or `ForIn` no longer require a proof that the underlying iterator is finite. If one wants to ensure the termination of them intrinsically, there are strictly terminating variants available as, for example, `it.ensureTermination.toList` instead of `it.toList`.
1 parent cbf6fe5 commit 383c0ca

File tree

38 files changed

+2225
-1143
lines changed

38 files changed

+2225
-1143
lines changed

src/Init/Data/Iterators/Basic.lean

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,16 @@ abbrev IterM.Step {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator
393393
(it : IterM (α := α) m β) :=
394394
PlausibleIterStep it.IsPlausibleStep
395395

396+
/--
397+
Makes a single step with the given iterator `it`, potentially emitting a value and providing a
398+
succeeding iterator. If this function is used recursively, termination can sometimes be proved with
399+
the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
400+
-/
401+
@[always_inline, inline, expose]
402+
def IterM.step {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator α m β]
403+
(it : IterM (α := α) m β) : m (Shrink it.Step) :=
404+
Iterator.step it
405+
396406
/--
397407
Asserts that a certain output value could plausibly be emitted by the given iterator in its next
398408
step.
@@ -420,16 +430,6 @@ def IterM.IsPlausibleSkipSuccessorOf {α : Type w} {m : Type w → Type w'} {β
420430
[Iterator α m β] (it' it : IterM (α := α) m β) : Prop :=
421431
it.IsPlausibleStep (.skip it')
422432

423-
/--
424-
Makes a single step with the given iterator `it`, potentially emitting a value and providing a
425-
succeeding iterator. If this function is used recursively, termination can sometimes be proved with
426-
the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
427-
-/
428-
@[always_inline, inline, expose]
429-
def IterM.step {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator α m β]
430-
(it : IterM (α := α) m β) : m (Shrink it.Step) :=
431-
Iterator.step it
432-
433433
end Monadic
434434

435435
section Pure
@@ -717,6 +717,15 @@ def IterM.finitelyManySteps {α : Type w} {m : Type w → Type w'} {β : Type w}
717717
[Finite α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Finite α m :=
718718
⟨it⟩
719719

720+
/--
721+
Termination measure to be used in well-founded recursive functions recursing over a finite iterator
722+
(see also `Finite`).
723+
-/
724+
@[expose]
725+
def IterM.finitelyManySteps! {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator α m β]
726+
(it : IterM (α := α) m β) : IterM.TerminationMeasures.Finite α m :=
727+
⟨it⟩
728+
720729
/--
721730
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
722731
with `IterM.finitelyManySteps`.

src/Init/Data/Iterators/Combinators/Monadic/Attach.lean

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,11 @@ instance Attach.instIteratorCollect {α β : Type w} {m : Type w → Type w'} [M
9191
IteratorCollect (Attach α m P) m n :=
9292
.defaultImplementation
9393

94-
instance Attach.instIteratorCollectPartial {α β : Type w} {m : Type w → Type w'} [Monad m]
95-
[Monad n] {P : β → Prop} [Iterator α m β] :
96-
IteratorCollectPartial (Attach α m P) m n :=
97-
.defaultImplementation
98-
9994
instance Attach.instIteratorLoop {α β : Type w} {m : Type w → Type w'} [Monad m]
10095
{n : Type x → Type x'} [Monad n] {P : β → Prop} [Iterator α m β] :
10196
IteratorLoop (Attach α m P) m n :=
10297
.defaultImplementation
10398

104-
instance Attach.instIteratorLoopPartial {α β : Type w} {m : Type w → Type w'} [Monad m]
105-
{n : Type x → Type x'} [Monad n] {P : β → Prop} [Iterator α m β] :
106-
IteratorLoopPartial (Attach α m P) m n :=
107-
.defaultImplementation
108-
10999
end Types
110100

111101
/--

src/Init/Data/Iterators/Combinators/Monadic/FilterMap.lean

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -221,25 +221,11 @@ instance {α β γ : Type w} {m : Type w → Type w'}
221221
IteratorCollect (FilterMap α m n lift f) n o :=
222222
.defaultImplementation
223223

224-
instance {α β γ : Type w} {m : Type w → Type w'}
225-
{n : Type w → Type w''} {o : Type w → Type x} [Monad n] [Monad o] [Iterator α m β]
226-
{lift : ⦃α : Type w⦄ → m α → n α}
227-
{f : β → PostconditionT n (Option γ)} [Finite α m] :
228-
IteratorCollectPartial (FilterMap α m n lift f) n o :=
229-
.defaultImplementation
230-
231224
instance FilterMap.instIteratorLoop {α β γ : Type w} {m : Type w → Type w'}
232-
{n : Type w → Type w''} {o : Type x → Type x'}
233-
[Monad n] [Monad o] [Iterator α m β] {lift : ⦃α : Type w⦄ → m α → n α}
234-
{f : β → PostconditionT n (Option γ)} [Finite α m] :
235-
IteratorLoop (FilterMap α m n lift f) n o :=
236-
.defaultImplementation
237-
238-
instance FilterMap.instIteratorLoopPartial {α β γ : Type w} {m : Type w → Type w'}
239225
{n : Type w → Type w''} {o : Type x → Type x'}
240226
[Monad n] [Monad o] [Iterator α m β] {lift : ⦃α : Type w⦄ → m α → n α}
241227
{f : β → PostconditionT n (Option γ)} :
242-
IteratorLoopPartial (FilterMap α m n lift f) n o :=
228+
IteratorLoop (FilterMap α m n lift f) n o :=
243229
.defaultImplementation
244230

245231
/--
@@ -249,7 +235,7 @@ instance FilterMap.instIteratorLoopPartial {α β γ : Type w} {m : Type w → T
249235
instance Map.instIteratorCollect {α β γ : Type w} {m : Type w → Type w'}
250236
{n : Type w → Type w''} {o : Type w → Type x} [Monad n] [Monad o] [Iterator α m β]
251237
{lift₁ : ⦃α : Type w⦄ → m α → n α}
252-
{f : β → PostconditionT n γ} [IteratorCollect α m o] [Finite α m] :
238+
{f : β → PostconditionT n γ} [IteratorCollect α m o] :
253239
IteratorCollect (Map α m n lift₁ f) n o where
254240
toArrayMapped lift₂ _ g it :=
255241
letI : MonadLift m n := ⟨lift₁ (α := _)⟩
@@ -259,32 +245,13 @@ instance Map.instIteratorCollect {α β γ : Type w} {m : Type w → Type w'}
259245
(fun x => do g (← (f x).operation))
260246
it.internalState.inner (m := m)
261247

262-
@[no_expose]
263-
instance Map.instIteratorCollectPartial {α β γ : Type w} {m : Type w → Type w'}
264-
{n : Type w → Type w''} {o : Type w → Type x} [Monad n] [Monad o] [Iterator α m β]
265-
{lift₁ : ⦃α : Type w⦄ → m α → n α}
266-
{f : β → PostconditionT n γ} [IteratorCollectPartial α m o] :
267-
IteratorCollectPartial (Map α m n lift₁ f) n o where
268-
toArrayMappedPartial lift₂ _ g it :=
269-
IteratorCollectPartial.toArrayMappedPartial
270-
(lift := fun ⦃_⦄ a => lift₂ (lift₁ a))
271-
(fun x => do g (← lift₂ (f x).operation))
272-
it.internalState.inner (m := m)
273-
274248
instance Map.instIteratorLoop {α β γ : Type w} {m : Type w → Type w'}
275249
{n : Type w → Type w''} {o : Type x → Type x'} [Monad n] [Monad o] [Iterator α m β]
276250
{lift : ⦃α : Type w⦄ → m α → n α}
277251
{f : β → PostconditionT n γ} :
278252
IteratorLoop (Map α m n lift f) n o :=
279253
.defaultImplementation
280254

281-
instance Map.instIteratorLoopPartial {α β γ : Type w} {m : Type w → Type w'}
282-
{n : Type w → Type w''} {o : Type x → Type x'} [Monad n] [Monad o] [Iterator α m β]
283-
{lift : ⦃α : Type w⦄ → m α → n α}
284-
{f : β → PostconditionT n γ} :
285-
IteratorLoopPartial (Map α m n lift f) n o :=
286-
.defaultImplementation
287-
288255
/--
289256
*Note: This is a very general combinator that requires an advanced understanding of monads, dependent
290257
types and termination proofs. The variants `map` and `mapM` are easier to use and sufficient

src/Init/Data/Iterators/Combinators/Monadic/FlatMap.lean

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public structure Flatten (α α₂ β : Type w) (m) where
3333
/--
3434
Internal iterator combinator that is used to implement all `flatMap` variants
3535
-/
36-
@[always_inline]
36+
@[always_inline, inline]
3737
def IterM.flattenAfter {α α₂ β : Type w} {m : Type w → Type w'} [Monad m]
3838
[Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
3939
(it₁ : IterM (α := α) m (IterM (α := α₂) m β)) (it₂ : Option (IterM (α := α₂) m β)) :=
@@ -75,7 +75,7 @@ iterator.
7575
7676
For each value emitted by the outer iterator `it₁`, this combinator calls `f`.
7777
-/
78-
@[always_inline]
78+
@[always_inline, inline]
7979
public def IterM.flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type w}
8080
{γ : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
8181
(f : β → m (IterM (α := α₂) m γ)) (it₁ : IterM (α := α) m β) (it₂ : Option (IterM (α := α₂) m γ)) :=
@@ -114,7 +114,7 @@ This combinator incurs an additional O(1) cost with each output of `it` or an in
114114
115115
For each value emitted by the outer iterator `it`, this combinator calls `f`.
116116
-/
117-
@[always_inline, expose]
117+
@[always_inline, inline, expose]
118118
public def IterM.flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
119119
{γ : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
120120
(f : β → m (IterM (α := α₂) m γ)) (it : IterM (α := α) m β) :=
@@ -156,7 +156,7 @@ iterator.
156156
157157
For each value emitted by the outer iterator `it₁`, this combinator calls `f`.
158158
-/
159-
@[always_inline]
159+
@[always_inline, inline]
160160
public def IterM.flatMapAfter {α : Type w} {β : Type w} {α₂ : Type w}
161161
{γ : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
162162
(f : β → IterM (α := α₂) m γ) (it₁ : IterM (α := α) m β) (it₂ : Option (IterM (α := α₂) m γ)) :=
@@ -195,7 +195,7 @@ This combinator incurs an additional O(1) cost with each output of `it` or an in
195195
196196
For each value emitted by the outer iterator `it`, this combinator calls `f`.
197197
-/
198-
@[always_inline, expose]
198+
@[always_inline, inline, expose]
199199
public def IterM.flatMap {α : Type w} {β : Type w} {α₂ : Type w}
200200
{γ : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
201201
(f : β → IterM (α := α₂) m γ) (it : IterM (α := α) m β) :=
@@ -370,16 +370,8 @@ public instance Flatten.instIteratorCollect [Monad m] [Monad n] [Iterator α m (
370370
[Iterator α₂ m β] : IteratorCollect (Flatten α α₂ β m) m n :=
371371
.defaultImplementation
372372

373-
public instance Flatten.instIteratorCollectPartial [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
374-
[Iterator α₂ m β] : IteratorCollectPartial (Flatten α α₂ β m) m n :=
375-
.defaultImplementation
376-
377373
public instance Flatten.instIteratorLoop [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
378374
[Iterator α₂ m β] : IteratorLoop (Flatten α α₂ β m) m n :=
379375
.defaultImplementation
380376

381-
public instance Flatten.instIteratorLoopPartial [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
382-
[Iterator α₂ m β] : IteratorLoopPartial (Flatten α α₂ β m) m n :=
383-
.defaultImplementation
384-
385377
end Std.Iterators

src/Init/Data/Iterators/Combinators/Monadic/Take.lean

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,8 @@ instance Take.instIteratorCollect {n : Type w → Type w'} [Monad m] [Monad n] [
208208
IteratorCollect (Take α m) m n :=
209209
.defaultImplementation
210210

211-
instance Take.instIteratorCollectPartial {n : Type w → Type w'} [Monad m] [Monad n] [Iterator α m β] :
212-
IteratorCollectPartial (Take α m) m n :=
213-
.defaultImplementation
214-
215211
instance Take.instIteratorLoop {n : Type x → Type x'} [Monad m] [Monad n] [Iterator α m β] :
216212
IteratorLoop (Take α m) m n :=
217213
.defaultImplementation
218214

219-
instance Take.instIteratorLoopPartial [Monad m] [Monad n] [Iterator α m β] :
220-
IteratorLoopPartial (Take α m) m n :=
221-
.defaultImplementation
222-
223215
end Std.Iterators

src/Init/Data/Iterators/Combinators/Monadic/ULift.lean

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,10 @@ instance Types.ULiftIterator.instIteratorLoop {o : Type x → Type x'} [Monad n]
128128
IteratorLoop (ULiftIterator α m n β lift) n o :=
129129
.defaultImplementation
130130

131-
instance Types.ULiftIterator.instIteratorLoopPartial {o : Type x → Type x'} [Monad n] [Monad o] [Iterator α m β] :
132-
IteratorLoopPartial (ULiftIterator α m n β lift) n o :=
133-
.defaultImplementation
134-
135131
instance Types.ULiftIterator.instIteratorCollect [Monad n] [Monad o] [Iterator α m β] :
136132
IteratorCollect (ULiftIterator α m n β lift) n o :=
137133
.defaultImplementation
138134

139-
instance Types.ULiftIterator.instIteratorCollectPartial {o} [Monad n] [Monad o] [Iterator α m β] :
140-
IteratorCollectPartial (ULiftIterator α m n β lift) n o :=
141-
.defaultImplementation
142-
143135
/--
144136
Transforms an `m`-monadic iterator with values in `β` into an `n`-monadic iterator with
145137
values in `ULift β`. Requires a `MonadLift m (ULiftT n)` instance.

src/Init/Data/Iterators/Consumers.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ public import Init.Data.Iterators.Consumers.Access
1111
public import Init.Data.Iterators.Consumers.Collect
1212
public import Init.Data.Iterators.Consumers.Loop
1313
public import Init.Data.Iterators.Consumers.Partial
14+
public import Init.Data.Iterators.Consumers.Total
1415

1516
public import Init.Data.Iterators.Consumers.Stream

src/Init/Data/Iterators/Consumers/Collect.lean

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module
77

88
prelude
99
public import Init.Data.Iterators.Consumers.Partial
10+
public import Init.Data.Iterators.Consumers.Total
1011
public import Init.Data.Iterators.Consumers.Monadic.Collect
1112

1213
@[expose] public section
@@ -21,40 +22,113 @@ Concretely, the following operations are provided:
2122
* `Iter.toListRev`, collecting the values in a list in reverse order but more efficiently
2223
* `Iter.toArray`, collecting the values in an array
2324
24-
Some operations are implemented using the `IteratorCollect` and `IteratorCollectPartial`
25-
typeclasses.
25+
Some operations are implemented using the `IteratorCollect` type class.
2626
-/
2727

2828
namespace Std.Iterators
2929

30-
@[always_inline, inline, inherit_doc IterM.toArray]
30+
/--
31+
Traverses the given iterator and stores the emitted values in an array.
32+
33+
If the iterator is not finite, this function might run forever. The variant
34+
`it.ensureTermination.toArray` always terminates after finitely many steps.
35+
-/
36+
@[always_inline, inline]
3137
def Iter.toArray {α : Type w} {β : Type w}
32-
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter (α := α) β) : Array β :=
38+
[Iterator α Id β] [IteratorCollect α Id Id] (it : Iter (α := α) β) : Array β :=
3339
it.toIterM.toArray.run
3440

35-
@[always_inline, inline, inherit_doc IterM.Partial.toArray]
41+
/--
42+
Traverses the given iterator and stores the emitted values in an array.
43+
44+
This function is deprecated. Instead of `it.allowNontermination.toArray`, use `it.toArray`.
45+
-/
46+
@[always_inline, inline, deprecated Iter.toArray (since := "2025-12-04")]
3647
def Iter.Partial.toArray {α : Type w} {β : Type w}
37-
[Iterator α Id β] [IteratorCollectPartial α Id Id] (it : Iter.Partial (α := α) β) : Array β :=
38-
it.it.toIterM.allowNontermination.toArray.run
48+
[Iterator α Id β] [IteratorCollect α Id Id] (it : Iter.Partial (α := α) β) : Array β :=
49+
it.it.toArray
50+
51+
/--
52+
Traverses the given iterator and stores the emitted values in an array.
3953
40-
@[always_inline, inline, inherit_doc IterM.toListRev]
54+
This variant terminates after finitely many steps and requires a proof that the iterator is
55+
finite. If such a proof is not available, consider using `Iter.toArray`.
56+
-/
57+
@[always_inline, inline]
58+
def Iter.Total.toArray {α : Type w} {β : Type w}
59+
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter.Total (α := α) β) :
60+
Array β :=
61+
it.it.toArray
62+
63+
/--
64+
Traverses the given iterator and stores the emitted values in reverse order in a list. Because
65+
lists are prepend-only, this `toListRev` is usually more efficient that `toList`.
66+
67+
If the iterator is not finite, this function might run forever. The variant
68+
`it.ensureTermination.toListRev` always terminates after finitely many steps.
69+
-/
70+
@[always_inline, inline]
4171
def Iter.toListRev {α : Type w} {β : Type w}
42-
[Iterator α Id β] [Finite α Id] (it : Iter (α := α) β) : List β :=
72+
[Iterator α Id β] (it : Iter (α := α) β) : List β :=
4373
it.toIterM.toListRev.run
4474

45-
@[always_inline, inline, inherit_doc IterM.Partial.toListRev]
75+
/--
76+
Traverses the given iterator and stores the emitted values in reverse order in a list. Because
77+
lists are prepend-only, this `toListRev` is usually more efficient that `toList`.
78+
79+
This function is deprecated. Instead of `it.allowNontermination.toListRev`, use `it.toListRev`.
80+
-/
81+
@[always_inline, inline, deprecated Iter.toListRev (since := "2025-12-04")]
4682
def Iter.Partial.toListRev {α : Type w} {β : Type w}
4783
[Iterator α Id β] (it : Iter.Partial (α := α) β) : List β :=
48-
it.it.toIterM.allowNontermination.toListRev.run
84+
it.it.toListRev
4985

50-
@[always_inline, inline, inherit_doc IterM.toList]
86+
/--
87+
Traverses the given iterator and stores the emitted values in reverse order in a list. Because
88+
lists are prepend-only, this `toListRev` is usually more efficient that `toList`.
89+
90+
This variant terminates after finitely many steps and requires a proof that the iterator is
91+
finite. If such a proof is not available, consider using `Iter.toListRev`.
92+
-/
93+
@[always_inline, inline]
94+
def Iter.Total.toListRev {α : Type w} {β : Type w}
95+
[Iterator α Id β] [Finite α Id] (it : Iter.Total (α := α) β) : List β :=
96+
it.it.toListRev
97+
98+
/--
99+
Traverses the given iterator and stores the emitted values in a list. Because
100+
lists are prepend-only, `toListRev` is usually more efficient that `toList`.
101+
102+
If the iterator is not finite, this function might run forever. The variant
103+
`it.ensureTermination.toList` always terminates after finitely many steps.
104+
-/
105+
@[always_inline, inline]
51106
def Iter.toList {α : Type w} {β : Type w}
52-
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter (α := α) β) : List β :=
107+
[Iterator α Id β] [IteratorCollect α Id Id] (it : Iter (α := α) β) : List β :=
53108
it.toIterM.toList.run
54109

55-
@[always_inline, inline, inherit_doc IterM.Partial.toList]
110+
/--
111+
Traverses the given iterator and stores the emitted values in a list. Because
112+
lists are prepend-only, `toListRev` is usually more efficient that `toList`.
113+
114+
This function is deprecated. Instead of `it.allowNontermination.toList`, use `it.toList`.
115+
-/
116+
@[always_inline, deprecated Iter.toList (since := "2025-12-04")]
56117
def Iter.Partial.toList {α : Type w} {β : Type w}
57-
[Iterator α Id β] [IteratorCollectPartial α Id Id] (it : Iter.Partial (α := α) β) : List β :=
58-
it.it.toIterM.allowNontermination.toList.run
118+
[Iterator α Id β] [IteratorCollect α Id Id] (it : Iter.Partial (α := α) β) : List β :=
119+
it.it.toList
120+
121+
/--
122+
Traverses the given iterator and stores the emitted values in a list. Because
123+
lists are prepend-only, `toListRev` is usually more efficient that `toList`.
124+
125+
This variant terminates after finitely many steps and requires a proof that the iterator is
126+
finite. If such a proof is not available, consider using `Iter.toList`.
127+
-/
128+
@[always_inline, inline]
129+
def Iter.Total.toList {α : Type w} {β : Type w}
130+
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter.Total (α := α) β) :
131+
List β :=
132+
it.it.toList
59133

60134
end Std.Iterators

0 commit comments

Comments
 (0)