Skip to content

Commit 9a3fb90

Browse files
authored
refactor: replace Iter(M).size with Iter(M).count (#10952)
This PR replaces `Iter(M).size` with the `Iter(M).count`. While the former used a special `IteratorSize` type class, the latter relies on `IteratorLoop`. The `IteratorSize` class is deprecated. The PR also renames lemmas about ranges be replacing `_Rcc` with `_rcc`, `_Rco` with `_roo` (and so on) in names, in order to be more consistent with the naming convention.
1 parent 7f7a4d3 commit 9a3fb90

File tree

34 files changed

+1250
-604
lines changed

34 files changed

+1250
-604
lines changed

src/Init/Data/Array/Lex/Lemmas.lean

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ private theorem cons_lex_cons [BEq α] {lt : α → α → Bool} {a b : α} {xs
7575
Nat.add_min_add_left, Nat.add_lt_add_iff_left, Std.Rco.forIn'_eq_forIn'_toList]
7676
conv =>
7777
lhs; congr; congr
78-
rw [cons_lex_cons.forIn'_congr_aux Std.Rco.toList_eq_if rfl (fun _ _ _ => rfl)]
78+
rw [cons_lex_cons.forIn'_congr_aux Std.Rco.toList_eq_if_roo rfl (fun _ _ _ => rfl)]
7979
simp only [bind_pure_comp, map_pure]
8080
rw [cons_lex_cons.forIn'_congr_aux (if_pos (by omega)) rfl (fun _ _ _ => rfl)]
81-
simp only [Std.toList_Roo_eq_toList_Rco_of_isSome_succ? (lo := 0) (h := rfl),
82-
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_Rco_succ_succ,
81+
simp only [Std.toList_roo_eq_toList_rco_of_isSome_succ? (lo := 0) (h := rfl),
82+
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_rco_succ_succ,
8383
Option.get_some, List.forIn'_cons, List.size_toArray, List.length_cons, List.length_nil,
8484
Nat.lt_add_one, getElem_append_left, List.getElem_toArray, List.getElem_cons_zero]
8585
cases lt a b

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,6 @@ instance Attach.instIteratorLoopPartial {α β : Type w} {m : Type w → Type w'
106106
IteratorLoopPartial (Attach α m P) m n :=
107107
.defaultImplementation
108108

109-
instance {α β : Type w} {m : Type w → Type w'} [Monad m]
110-
{P : β → Prop} [Iterator α m β] [IteratorSize α m] :
111-
IteratorSize (Attach α m P) m where
112-
size it := IteratorSize.size it.internalState.inner
113-
114-
instance {α β : Type w} {m : Type w → Type w'} [Monad m]
115-
{P : β → Prop} [Iterator α m β] [IteratorSizePartial α m] :
116-
IteratorSizePartial (Attach α m P) m where
117-
size it := IteratorSizePartial.size it.internalState.inner
118-
119109
end Types
120110

121111
/--

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

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -604,30 +604,4 @@ def IterM.filter {α β : Type w} {m : Type w → Type w'} [Iterator α m β] [M
604604
(f : β → Bool) (it : IterM (α := α) m β) :=
605605
(it.filterMap (fun b => if f b then some b else none) : IterM m β)
606606

607-
instance {α β γ : Type w} {m : Type w → Type w'}
608-
{n : Type w → Type w''} [Monad n] [Iterator α m β] {lift : ⦃α : Type w⦄ → m α → n α}
609-
{f : β → PostconditionT n (Option γ)} [Finite α m] :
610-
IteratorSize (FilterMap α m n lift f) n :=
611-
.defaultImplementation
612-
613-
instance {α β γ : Type w} {m : Type w → Type w'}
614-
{n : Type w → Type w''} [Monad n] [Iterator α m β] {lift : ⦃α : Type w⦄ → m α → n α}
615-
{f : β → PostconditionT n (Option γ)} :
616-
IteratorSizePartial (FilterMap α m n lift f) n :=
617-
.defaultImplementation
618-
619-
instance {α β γ : Type w} {m : Type w → Type w'}
620-
{n : Type w → Type w''} [Monad n] [Iterator α m β]
621-
{lift : ⦃α : Type w⦄ → m α → n α}
622-
{f : β → PostconditionT n γ} [IteratorSize α m] :
623-
IteratorSize (Map α m n lift f) n where
624-
size it := lift (IteratorSize.size it.internalState.inner)
625-
626-
instance {α β γ : Type w} {m : Type w → Type w'}
627-
{n : Type w → Type w''} [Monad n] [Iterator α m β]
628-
{lift : ⦃α : Type w⦄ → m α → n α}
629-
{f : β → PostconditionT n γ} [IteratorSizePartial α m] :
630-
IteratorSizePartial (Map α m n lift f) n where
631-
size it := lift (IteratorSizePartial.size it.internalState.inner)
632-
633607
end Std.Iterators

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ variable {α : Type u} {m : Type u → Type u'} {n : Type max u v → Type v'}
7474
/--
7575
Transforms a step of the base iterator into a step of the `uLift` iterator.
7676
-/
77-
@[always_inline, inline]
77+
@[always_inline, inline, expose]
7878
def Types.ULiftIterator.Monadic.modifyStep (step : IterStep (IterM (α := α) m β) β) :
7979
IterStep (IterM (α := ULiftIterator.{v} α m n β lift) n (ULift.{v} β)) (ULift.{v} β) :=
8080
match step with
@@ -140,15 +140,6 @@ instance Types.ULiftIterator.instIteratorCollectPartial {o} [Monad n] [Monad o]
140140
IteratorCollectPartial (ULiftIterator α m n β lift) n o :=
141141
.defaultImplementation
142142

143-
instance Types.ULiftIterator.instIteratorSize [Monad n] [Iterator α m β] [IteratorSize α m]
144-
[Finite (ULiftIterator α m n β lift) n] :
145-
IteratorSize (ULiftIterator α m n β lift) n :=
146-
.defaultImplementation
147-
148-
instance Types.ULiftIterator.instIteratorSizePartial [Monad n] [Iterator α m β] [IteratorSize α m] :
149-
IteratorSizePartial (ULiftIterator α m n β lift) n :=
150-
.defaultImplementation
151-
152143
/--
153144
Transforms an `m`-monadic iterator with values in `β` into an `n`-monadic iterator with
154145
values in `ULift β`. Requires a `MonadLift m (ULiftT n)` instance.

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

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -255,28 +255,52 @@ def Iter.Partial.find? {α β : Type w} [Iterator α Id β] [IteratorLoopPartial
255255
(it : Iter.Partial (α := α) β) (f : β → Bool) : Option β :=
256256
Id.run (it.findM? (pure <| .up <| f ·))
257257

258-
@[always_inline, inline, expose, inherit_doc IterM.size]
259-
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorSize α Id]
258+
/--
259+
Steps through the whole iterator, counting the number of outputs emitted.
260+
261+
**Performance**:
262+
263+
This function's runtime is linear in the number of steps taken by the iterator.
264+
-/
265+
@[always_inline, inline, expose]
266+
def Iter.count {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
260267
(it : Iter (α := α) β) : Nat :=
261-
(IteratorSize.size it.toIterM).run.down
268+
it.toIterM.count.run.down
269+
270+
/--
271+
Steps through the whole iterator, counting the number of outputs emitted.
272+
273+
**Performance**:
262274
263-
@[always_inline, inline, inherit_doc IterM.Partial.size]
264-
def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorSizePartial α Id]
275+
This function's runtime is linear in the number of steps taken by the iterator.
276+
-/
277+
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-10-29")]
278+
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
265279
(it : Iter (α := α) β) : Nat :=
266-
(IteratorSizePartial.size it.toIterM).run.down
280+
it.count
267281

268282
/--
269-
`LawfulIteratorSize α m` ensures that the `size` function of an iterator behaves as if it
270-
iterated over the whole iterator, counting its elements and causing all the monadic side effects
271-
of the iterations. This is a fairly strong condition for monadic iterators and it will be false
272-
for many efficient implementations of `size` that compute the size without actually iterating.
283+
Steps through the whole iterator, counting the number of outputs emitted.
284+
285+
**Performance**:
286+
287+
This function's runtime is linear in the number of steps taken by the iterator.
288+
-/
289+
@[always_inline, inline, expose]
290+
def Iter.Partial.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
291+
(it : Iter.Partial (α := α) β) : Nat :=
292+
it.it.toIterM.allowNontermination.count.run.down
293+
294+
/--
295+
Steps through the whole iterator, counting the number of outputs emitted.
296+
297+
**Performance**:
273298
274-
This class is experimental and users of the iterator API should not explicitly depend on it.
299+
This function's runtime is linear in the number of steps taken by the iterator.
275300
-/
276-
class LawfulIteratorSize (α : Type w) {β : Type w} [Iterator α Id β] [Finite α Id]
277-
[IteratorSize α Id] where
278-
size_eq_size_toArray {it : Iter (α := α) β} : it.size =
279-
haveI : IteratorCollect α Id Id := .defaultImplementation
280-
it.toArray.size
301+
@[always_inline, inline, expose, deprecated Iter.Partial.count (since := "2025-10-29")]
302+
def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
303+
(it : Iter.Partial (α := α) β) : Nat :=
304+
it.count
281305

282306
end Std.Iterators

src/Init/Data/Iterators/Consumers/Monadic/Loop.lean

Lines changed: 30 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -88,27 +88,6 @@ class IteratorLoopPartial (α : Type w) (m : Type w → Type w') {β : Type w} [
8888
(it : IterM (α := α) m β) → γ →
8989
((b : β) → it.IsPlausibleIndirectOutput b → (c : γ) → n (ForInStep γ)) → n γ
9090

91-
/--
92-
`IteratorSize α m` provides an implementation of the `IterM.size` function.
93-
94-
This class is experimental and users of the iterator API should not explicitly depend on it.
95-
They can, however, assume that consumers that require an instance will work for all iterators
96-
provided by the standard library.
97-
-/
98-
class IteratorSize (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] where
99-
size : IterM (α := α) m β → m (ULift Nat)
100-
101-
/--
102-
`IteratorSizePartial α m` provides an implementation of the `IterM.Partial.size` function that
103-
can be used as `it.allowTermination.size`.
104-
105-
This class is experimental and users of the iterator API should not explicitly depend on it.
106-
They can, however, assume that consumers that require an instance will work for all iterators
107-
provided by the standard library.
108-
-/
109-
class IteratorSizePartial (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] where
110-
size : IterM (α := α) m β → m (ULift Nat)
111-
11291
end Typeclasses
11392

11493
/-- Internal implementation detail of the iterator library. -/
@@ -315,7 +294,7 @@ instance {m : Type w → Type w'} {n : Type w → Type w''}
315294
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
316295

317296
instance {m : Type w → Type w'} {n : Type w → Type w''}
318-
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoopPartial α m n]
297+
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoopPartial α m n]
319298
[MonadLiftT m n] :
320299
ForM n (IterM.Partial (α := α) m β) β where
321300
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
@@ -633,86 +612,58 @@ def IterM.Partial.find? {α β : Type w} {m : Type w → Type w'} [Monad m] [Ite
633612
m (Option β) :=
634613
it.findM? (pure <| .up <| f ·)
635614

636-
section Size
615+
section Count
637616

638617
/--
639-
This is the implementation of the default instance `IteratorSize.defaultImplementation`.
640-
-/
641-
@[always_inline, inline]
642-
def IterM.DefaultConsumers.size {α : Type w} {m : Type w → Type w'} [Monad m] {β : Type w}
643-
[Iterator α m β] [IteratorLoop α m m] [Finite α m] (it : IterM (α := α) m β) :
644-
m (ULift Nat) :=
645-
it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
618+
Steps through the whole iterator, counting the number of outputs emitted.
646619
647-
/--
648-
This is the implementation of the default instance `IteratorSizePartial.defaultImplementation`.
620+
**Performance**:
621+
622+
This function's runtime is linear in the number of steps taken by the iterator.
649623
-/
650624
@[always_inline, inline]
651-
def IterM.DefaultConsumers.sizePartial {α : Type w} {m : Type w → Type w'} [Monad m] {β : Type w}
652-
[Iterator α m β] [IteratorLoopPartial α m m] (it : IterM (α := α) m β) :
653-
m (ULift Nat) :=
654-
it.allowNontermination.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
625+
def IterM.count {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator α m β] [Finite α m]
626+
[IteratorLoop α m m]
627+
[Monad m] (it : IterM (α := α) m β) : m (ULift Nat) :=
628+
it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
655629

656630
/--
657-
This is the default implementation of the `IteratorSize` class.
658-
It simply iterates using `IteratorLoop` and counts the elements.
659-
For certain iterators, more efficient implementations are possible and should be used instead.
660-
-/
661-
@[always_inline, inline]
662-
def IteratorSize.defaultImplementation {α β : Type w} {m : Type w → Type w'} [Monad m]
663-
[Iterator α m β] [Finite α m] [IteratorLoop α m m] :
664-
IteratorSize α m where
665-
size := IterM.DefaultConsumers.size
631+
Steps through the whole iterator, counting the number of outputs emitted.
666632
633+
**Performance**:
667634
668-
/--
669-
This is the default implementation of the `IteratorSizePartial` class.
670-
It simply iterates using `IteratorLoopPartial` and counts the elements.
671-
For certain iterators, more efficient implementations are possible and should be used instead.
635+
This function's runtime is linear in the number of steps taken by the iterator.
672636
-/
673-
@[always_inline, inline]
674-
instance IteratorSizePartial.defaultImplementationβ : Type w} {m : Type w → Type w'} [Monad m]
675-
[Iterator α m β] [IteratorLoopPartial α m m] :
676-
IteratorSizePartial α m where
677-
size := IterM.DefaultConsumers.sizePartial
637+
@[always_inline, inline, deprecated IterM.count (since := "2025-10-29")]
638+
def IterM.size {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator α m β] [Finite α m]
639+
[IteratorLoop α m m]
640+
[Monad m] (it : IterM (α := α) m β) : m (ULift Nat) :=
641+
it.count
678642

679643
/--
680-
Computes how many elements the iterator returns. In monadic situations, it is unclear which effects
681-
are caused by calling `size`, and if the monad is nondeterministic, it is also unclear what the
682-
returned value should be. The reference implementation, `IteratorSize.defaultImplementation`,
683-
simply iterates over the whole iterator monadically, counting the number of emitted values.
684-
An `IteratorSize` instance is considered lawful if it is equal to the reference implementation.
644+
Steps through the whole iterator, counting the number of outputs emitted.
685645
686646
**Performance**:
687647
688-
Default performance is linear in the number of steps taken by the iterator.
648+
This function's runtime is linear in the number of steps taken by the iterator.
689649
-/
690650
@[always_inline, inline]
691-
def IterM.size {α : Type} {m : TypeType w'} {β : Type} [Iterator α m β] [Monad m]
692-
(it : IterM (α := α) m β) [IteratorSize α m] : m Nat :=
693-
ULift.down <$> IteratorSize.size it
651+
def IterM.Partial.count {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
652+
[IteratorLoopPartial α m m] [Monad m] (it : IterM.Partial (α := α) m β) : m (ULift Nat) :=
653+
it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
694654

695655
/--
696-
Computes how many elements the iterator emits.
697-
698-
With monadic iterators (`IterM`), it is unclear which effects
699-
are caused by calling `size`, and if the monad is nondeterministic, it is also unclear what the
700-
returned value should be. The reference implementation, `IteratorSize.defaultImplementation`,
701-
simply iterates over the whole iterator monadically, counting the number of emitted values.
702-
An `IteratorSize` instance is considered lawful if it is equal to the reference implementation.
703-
704-
This is the partial version of `size`. It does not require a proof of finiteness and might loop
705-
forever. It is not possible to verify the behavior in Lean because it uses `partial`.
656+
Steps through the whole iterator, counting the number of outputs emitted.
706657
707658
**Performance**:
708659
709-
Default performance is linear in the number of steps taken by the iterator.
660+
This function's runtime is linear in the number of steps taken by the iterator.
710661
-/
711-
@[always_inline, inline]
712-
def IterM.Partial.size {α : Type} {m : TypeType w'} {β : Type} [Iterator α m β] [Monad m]
713-
(it : IterM.Partial (α := α) m β) [IteratorSizePartial α m] : m Nat :=
714-
ULift.down <$> IteratorSizePartial.size it.it
662+
@[always_inline, inline, deprecated IterM.Partial.count (since := "2025-10-29")]
663+
def IterM.Partial.size {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
664+
[IteratorLoopPartial α m m] [Monad m] (it : IterM.Partial (α := α) m β) : m (ULift Nat) :=
665+
it.count
715666

716-
end Size
667+
end Count
717668

718669
end Std.Iterators

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import all Init.Data.Iterators.Combinators.Attach
1111
import all Init.Data.Iterators.Combinators.Monadic.Attach
1212
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Attach
1313
public import Init.Data.Iterators.Lemmas.Consumers.Collect
14+
public import Init.Data.Iterators.Lemmas.Consumers.Loop
1415
public import Init.Data.Array.Attach
1516

1617
public section
@@ -82,4 +83,14 @@ theorem Iter.toArray_attachWith [Iterator α Id β]
8283
simpa only [Array.toList_inj]
8384
simp [Iter.toList_toArray]
8485

86+
@[simp]
87+
theorem Iter.count_attachWith [Iterator α Id β]
88+
{it : Iter (α := α) β} {hP}
89+
[Finite α Id] [IteratorLoop α Id Id]
90+
[LawfulIteratorLoop α Id Id] :
91+
(it.attachWith P hP).count = it.count := by
92+
letI : IteratorCollect α Id Id := .defaultImplementation
93+
rw [← Iter.length_toList_eq_count, toList_attachWith]
94+
simp
95+
8596
end Std.Iterators

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,17 @@ theorem Iter.fold_map {α β γ : Type w} {δ : Type x}
467467

468468
end Fold
469469

470+
section Count
471+
472+
@[simp]
473+
theorem Iter.count_map {α β β' : Type w} [Iterator α Id β]
474+
[IteratorLoop α Id Id] [Finite α Id] [LawfulIteratorLoop α Id Id]
475+
{it : Iter (α := α) β} {f : β → β'} :
476+
(it.map f).count = it.count := by
477+
simp [map_eq_toIter_map_toIterM, count_eq_count_toIterM]
478+
479+
end Count
480+
470481
theorem Iter.anyM_filterMapM {α β β' : Type w} {m : Type w → Type w'}
471482
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
472483
{it : Iter (α := α) β} {f : β → m (Option β')} {p : β' → m (ULift Bool)} :

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ prelude
99
public import Init.Data.Iterators.Combinators.Monadic.Attach
1010
import all Init.Data.Iterators.Combinators.Monadic.Attach
1111
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
12+
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
1213

1314
public section
1415

@@ -59,4 +60,14 @@ theorem IterM.map_unattach_toArray_attachWith [Iterator α m β] [Monad m] [Mona
5960
rw [← toArray_toList, ← toArray_toList, ← map_unattach_toList_attachWith (it := it) (hP := hP)]
6061
simp [-map_unattach_toList_attachWith, -IterM.toArray_toList]
6162

63+
@[simp]
64+
theorem IterM.count_attachWith [Iterator α m β] [Monad m] [Monad n]
65+
{it : IterM (α := α) m β} {hP}
66+
[Finite α m] [IteratorLoop α m m] [LawfulMonad m] [LawfulIteratorLoop α m m] :
67+
(it.attachWith P hP).count = it.count := by
68+
letI : IteratorCollect α m m := .defaultImplementation
69+
rw [← up_length_toList_eq_count, ← up_length_toList_eq_count,
70+
← map_unattach_toList_attachWith (it := it) (P := P) (hP := hP)]
71+
simp only [Functor.map_map, List.length_unattach]
72+
6273
end Std.Iterators

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,23 @@ theorem IterM.fold_map {α β γ δ : Type w} {m : Type w → Type w'}
895895

896896
end Fold
897897

898+
section Count
899+
900+
@[simp]
901+
theorem IterM.count_map {α β β' : Type w} {m : Type w → Type w'} [Iterator α m β] [Monad m]
902+
[IteratorLoop α m m] [Finite α m] [LawfulMonad m] [LawfulIteratorLoop α m m]
903+
{it : IterM (α := α) m β} {f : β → β'} :
904+
(it.map f).count = it.count := by
905+
induction it using IterM.inductSteps with | step it ihy ihs
906+
rw [count_eq_match_step, count_eq_match_step, step_map, bind_assoc]
907+
apply bind_congr; intro step
908+
cases step.inflate using PlausibleIterStep.casesOn
909+
· simp [ihy ‹_›]
910+
· simp [ihs ‹_›]
911+
· simp
912+
913+
end Count
914+
898915
section AnyAll
899916

900917
theorem IterM.anyM_filterMapM {α β β' : Type w} {m : Type w → Type w'} {n : Type w → Type w''}

0 commit comments

Comments
 (0)