/- Copyright (c) 2025 Lean FRO, LLC. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Paul Reichert -/ module prelude public import Init.RCases public import Init.Data.Iterators.Basic public import Init.Data.Iterators.Consumers.Monadic.Partial public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction public section /-! # Loop-based consumers This module provides consumers that iterate over a given iterator, applying a certain user-supplied function in every iteration. Concretely, the following operations are provided: * `ForIn` instances * `IterM.fold`, the analogue of `List.foldl` * `IterM.foldM`, the analogue of `List.foldlM` * `IterM.drain`, which iterates over the whole iterator and discards all emitted values. It can be used to apply the monadic effects of the iterator. Some producers and combinators provide specialized implementations. These are captured by the `IteratorLoop` and `IteratorLoopPartial` typeclasses. They should be implemented by all types of iterators. A default implementation is provided. The typeclass `LawfulIteratorLoop` asserts that an `IteratorLoop` instance equals the default implementation. -/ namespace Std.Iterators open Std.Internal section Typeclasses /-- Relation that needs to be well-formed in order for a loop over an iterator to terminate. It is assumed that the `plausible_forInStep` predicate relates the input and output of the stepper function. -/ @[expose] def IteratorLoop.rel (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] {γ : Type x} (plausible_forInStep : β → γ → ForInStep γ → Prop) (p' p : IterM (α := α) m β × γ) : Prop := (∃ b, p.1.IsPlausibleStep (.yield p'.1 b) ∧ plausible_forInStep b p.2 (.yield p'.2)) ∨ (p.1.IsPlausibleStep (.skip p'.1) ∧ p'.2 = p.2) /-- Asserts that `IteratorLoop.rel` is well-founded. -/ @[expose] def IteratorLoop.WellFounded (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] {γ : Type x} (plausible_forInStep : β → γ → ForInStep γ → Prop) : Prop := _root_.WellFounded (IteratorLoop.rel α m plausible_forInStep) /-- `IteratorLoop α m` provides efficient implementations of loop-based consumers for `α`-based iterators. The basis is a `ForIn`-style loop construct with the complication that it can be used for infinite iterators, too -- given a proof that the given loop will nevertheless terminate. This class is experimental and users of the iterator API should not explicitly depend on it. They can, however, assume that consumers that require an instance will work for all iterators provided by the standard library. -/ @[ext] class IteratorLoop (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] (n : Type x → Type x') where forIn : ∀ (_liftBind : (γ : Type w) → (δ : Type x) → (γ → n δ) → m γ → n δ) (γ : Type x), (plausible_forInStep : β → γ → ForInStep γ → Prop) → IteratorLoop.WellFounded α m plausible_forInStep → (it : IterM (α := α) m β) → γ → ((b : β) → it.IsPlausibleIndirectOutput b → (c : γ) → n (Subtype (plausible_forInStep b c))) → n γ /-- `IteratorLoopPartial α m` provides efficient implementations of loop-based consumers for `α`-based iterators. The basis is a partial, i.e. potentially nonterminating, `ForIn` instance. This class is experimental and users of the iterator API should not explicitly depend on it. They can, however, assume that consumers that require an instance will work for all iterators provided by the standard library. -/ class IteratorLoopPartial (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] (n : Type x → Type x') where forInPartial : ∀ (_liftBind : (γ : Type w) → (δ : Type x) → (γ → n δ) → m γ → n δ) {γ : Type x}, (it : IterM (α := α) m β) → γ → ((b : β) → it.IsPlausibleIndirectOutput b → (c : γ) → n (ForInStep γ)) → n γ /-- `IteratorSize α m` provides an implementation of the `IterM.size` function. This class is experimental and users of the iterator API should not explicitly depend on it. They can, however, assume that consumers that require an instance will work for all iterators provided by the standard library. -/ class IteratorSize (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] where size : IterM (α := α) m β → m (ULift Nat) /-- `IteratorSizePartial α m` provides an implementation of the `IterM.Partial.size` function that can be used as `it.allowTermination.size`. This class is experimental and users of the iterator API should not explicitly depend on it. They can, however, assume that consumers that require an instance will work for all iterators provided by the standard library. -/ class IteratorSizePartial (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] where size : IterM (α := α) m β → m (ULift Nat) end Typeclasses /-- Internal implementation detail of the iterator library. -/ structure IteratorLoop.WithWF (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] {γ : Type x} (PlausibleForInStep : β → γ → ForInStep γ → Prop) (hwf : IteratorLoop.WellFounded α m PlausibleForInStep) where it : IterM (α := α) m β acc : γ instance IteratorLoop.WithWF.instWellFoundedRelation (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] {γ : Type x} (PlausibleForInStep : β → γ → ForInStep γ → Prop) (hwf : IteratorLoop.WellFounded α m PlausibleForInStep) : WellFoundedRelation (WithWF α m PlausibleForInStep hwf) where rel := InvImage (IteratorLoop.rel α m PlausibleForInStep) (fun x => (x.it, x.acc)) wf := by exact InvImage.wf _ hwf /-- This is the loop implementation of the default instance `IteratorLoop.defaultImplementation`. -/ @[specialize, expose] def IterM.DefaultConsumers.forIn' {m : Type w → Type w'} {α : Type w} {β : Type w} [Iterator α m β] {n : Type x → Type x'} [Monad n] (lift : ∀ γ δ, (γ → n δ) → m γ → n δ) (γ : Type x) (plausible_forInStep : β → γ → ForInStep γ → Prop) (wf : IteratorLoop.WellFounded α m plausible_forInStep) (it : IterM (α := α) m β) (init : γ) (P : β → Prop) (hP : ∀ b, it.IsPlausibleIndirectOutput b → P b) (f : (b : β) → P b → (c : γ) → n (Subtype (plausible_forInStep b c))) : n γ := haveI : WellFounded _ := wf (lift _ _ · it.step) fun | .yield it' out h => do match ← f out (hP _ <| .direct ⟨_, h⟩) init with | ⟨.yield c, _⟩ => IterM.DefaultConsumers.forIn' lift _ plausible_forInStep wf it' c P (fun _ h' => hP _ <| .indirect ⟨_, rfl, h⟩ h') f | ⟨.done c, _⟩ => return c | .skip it' h => IterM.DefaultConsumers.forIn' lift _ plausible_forInStep wf it' init P (fun _ h' => hP _ <| .indirect ⟨_, rfl, h⟩ h') f | .done _ => return init termination_by IteratorLoop.WithWF.mk it init (hwf := wf) decreasing_by · exact Or.inl ⟨out, ‹_›, ‹_›⟩ · exact Or.inr ⟨‹_›, rfl⟩ theorem IterM.DefaultConsumers.forIn'_eq_forIn' {m : Type w → Type w'} {α : Type w} {β : Type w} [Iterator α m β] {n : Type x → Type x'} [Monad n] {lift : ∀ γ δ, (γ → n δ) → m γ → n δ} {γ : Type x} {Pl : β → γ → ForInStep γ → Prop} {wf : IteratorLoop.WellFounded α m Pl} {it : IterM (α := α) m β} {init : γ} {P : β → Prop} {hP : ∀ b, it.IsPlausibleIndirectOutput b → P b} {Q : β → Prop} {hQ : ∀ b, it.IsPlausibleIndirectOutput b → Q b} {f : (b : β) → P b → (c : γ) → n (Subtype (Pl b c))} {g : (b : β) → Q b → (c : γ) → n (Subtype (Pl b c))} (hfg : ∀ b c, (hPb : P b) → (hQb : Q b) → f b hPb c = g b hQb c) : IterM.DefaultConsumers.forIn' lift γ Pl wf it init P hP f = IterM.DefaultConsumers.forIn' lift γ Pl wf it init Q hQ g := by rw [forIn', forIn'] congr; ext step split · congr · apply hfg · ext split · apply IterM.DefaultConsumers.forIn'_eq_forIn' assumption · rfl · apply IterM.DefaultConsumers.forIn'_eq_forIn' assumption · rfl termination_by IteratorLoop.WithWF.mk it init (hwf := wf) decreasing_by · exact Or.inl ⟨_, ‹_›, ‹_›⟩ · exact Or.inr ⟨‹_›, rfl⟩ /-- This is the default implementation of the `IteratorLoop` class. It simply iterates through the iterator using `IterM.step`. For certain iterators, more efficient implementations are possible and should be used instead. -/ @[always_inline, inline, expose] def IteratorLoop.defaultImplementation {α : Type w} {m : Type w → Type w'} {n : Type x → Type x'} [Monad n] [Iterator α m β] : IteratorLoop α m n where forIn lift γ Pl wf it init := IterM.DefaultConsumers.forIn' lift γ Pl wf it init _ (fun _ => id) /-- Asserts that a given `IteratorLoop` instance is equal to `IteratorLoop.defaultImplementation`. (Even though equal, the given instance might be vastly more efficient.) -/ class LawfulIteratorLoop (α : Type w) (m : Type w → Type w') (n : Type x → Type x') [Monad m] [Monad n] [Iterator α m β] [i : IteratorLoop α m n] where lawful : ∀ lift [LawfulMonadLiftBindFunction lift], i.forIn lift = IteratorLoop.defaultImplementation.forIn lift /-- This is the loop implementation of the default instance `IteratorLoopPartial.defaultImplementation`. -/ @[specialize] partial def IterM.DefaultConsumers.forInPartial {m : Type w → Type w'} {α : Type w} {β : Type w} [Iterator α m β] {n : Type x → Type x'} [Monad n] (lift : ∀ γ δ, (γ → n δ) → m γ → n δ) (γ : Type x) (it : IterM (α := α) m β) (init : γ) (f : (b : β) → it.IsPlausibleIndirectOutput b → (c : γ) → n (ForInStep γ)) : n γ := (lift _ _ · it.step) fun | .yield it' out h => do match ← f out (.direct ⟨_, h⟩) init with | .yield c => IterM.DefaultConsumers.forInPartial lift _ it' c fun out h' acc => f out (.indirect ⟨_, rfl, h⟩ h') acc | .done c => return c | .skip it' h => IterM.DefaultConsumers.forInPartial lift _ it' init fun out h' acc => f out (.indirect ⟨_, rfl, h⟩ h') acc | .done _ => return init /-- This is the default implementation of the `IteratorLoopPartial` class. It simply iterates through the iterator using `IterM.step`. For certain iterators, more efficient implementations are possible and should be used instead. -/ @[always_inline, inline] def IteratorLoopPartial.defaultImplementation {α : Type w} {m : Type w → Type w'} {n : Type x → Type x'} [Monad m] [Monad n] [Iterator α m β] : IteratorLoopPartial α m n where forInPartial lift := IterM.DefaultConsumers.forInPartial lift _ instance (α : Type w) (m : Type w → Type w') (n : Type x → Type x') [Monad m] [Monad n] [Iterator α m β] [Finite α m] : letI : IteratorLoop α m n := .defaultImplementation LawfulIteratorLoop α m n := letI : IteratorLoop α m n := .defaultImplementation ⟨fun _ => rfl⟩ theorem IteratorLoop.wellFounded_of_finite {m : Type w → Type w'} {α β : Type w} {γ : Type x} [Iterator α m β] [Finite α m] : WellFounded α m (γ := γ) fun _ _ _ => True := by apply Subrelation.wf (r := InvImage IterM.TerminationMeasures.Finite.Rel (fun p => p.1.finitelyManySteps)) · intro p' p h apply Relation.TransGen.single obtain ⟨b, h, _⟩ | ⟨h, _⟩ := h · exact ⟨.yield p'.fst b, rfl, h⟩ · exact ⟨.skip p'.fst, rfl, h⟩ · apply InvImage.wf exact WellFoundedRelation.wf /-- This `ForIn'`-style loop construct traverses a finite iterator using an `IteratorLoop` instance. -/ @[always_inline, inline] def IteratorLoop.finiteForIn' {m : Type w → Type w'} {n : Type x → Type x'} {α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] (lift : ∀ γ δ, (γ → n δ) → m γ → n δ) : ForIn' n (IterM (α := α) m β) β ⟨fun it out => it.IsPlausibleIndirectOutput out⟩ where forIn' {γ} [Monad n] it init f := IteratorLoop.forIn (α := α) (m := m) lift γ (fun _ _ _ => True) wellFounded_of_finite it init (fun out h acc => (⟨·, .intro⟩) <$> f out h acc) /-- A `ForIn'` instance for iterators. Its generic membership relation is not easy to use, so this is not marked as `instance`. This way, more convenient instances can be built on top of it or future library improvements will make it more comfortable. -/ @[always_inline, inline] def IterM.instForIn' {m : Type w → Type w'} {n : Type w → Type w''} {α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] [Monad n] [MonadLiftT m n] : ForIn' n (IterM (α := α) m β) β ⟨fun it out => it.IsPlausibleIndirectOutput out⟩ := IteratorLoop.finiteForIn' (fun _ _ f x => monadLift x >>= f) instance {m : Type w → Type w'} {n : Type w → Type w''} {α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] [MonadLiftT m n] [Monad n] : ForIn n (IterM (α := α) m β) β := haveI : ForIn' n (IterM (α := α) m β) β _ := IterM.instForIn' instForInOfForIn' @[always_inline, inline] def IterM.Partial.instForIn' {m : Type w → Type w'} {n : Type w → Type w''} {α : Type w} {β : Type w} [Iterator α m β] [IteratorLoopPartial α m n] [MonadLiftT m n] [Monad n] : ForIn' n (IterM.Partial (α := α) m β) β ⟨fun it out => it.it.IsPlausibleIndirectOutput out⟩ where forIn' it init f := IteratorLoopPartial.forInPartial (α := α) (m := m) (n := n) (fun _ _ f x => monadLift x >>= f) it.it init f instance {m : Type w → Type w'} {n : Type w → Type w''} {α : Type w} {β : Type w} [Iterator α m β] [IteratorLoopPartial α m n] [MonadLiftT m n] [Monad n] : ForIn n (IterM.Partial (α := α) m β) β := haveI : ForIn' n (IterM.Partial (α := α) m β) β _ := IterM.Partial.instForIn' instForInOfForIn' instance {m : Type w → Type w'} {n : Type w → Type w''} {α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] [MonadLiftT m n] : ForM n (IterM (α := α) m β) β where forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit) instance {m : Type w → Type w'} {n : Type w → Type w''} {α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoopPartial α m n] [MonadLiftT m n] : ForM n (IterM.Partial (α := α) m β) β where forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit) /-- Folds a monadic function over an iterator from the left, accumulating a value starting with `init`. The accumulated value is combined with the each element of the list in order, using `f`. The monadic effects of `f` are interleaved with potential effects caused by the iterator's step function. Therefore, it may *not* be equivalent to `(← it.toList).foldlM`. This function requires a `Finite` instance proving that the iterator will finish after a finite number of steps. If the iterator is not finite or such an instance is not available, consider using `it.allowNontermination.foldM` instead of `it.foldM`. However, it is not possible to formally verify the behavior of the partial variant. -/ @[always_inline, inline] def IterM.foldM {m : Type w → Type w'} {n : Type w → Type w''} [Monad n] {α : Type w} {β : Type w} {γ : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] [MonadLiftT m n] (f : γ → β → n γ) (init : γ) (it : IterM (α := α) m β) : n γ := ForIn.forIn it init (fun x acc => ForInStep.yield <$> f acc x) /-- Folds a monadic function over an iterator from the left, accumulating a value starting with `init`. The accumulated value is combined with the each element of the list in order, using `f`. The monadic effects of `f` are interleaved with potential effects caused by the iterator's step function. Therefore, it may *not* be equivalent to `it.toList.foldlM`. This is a partial, potentially nonterminating, function. It is not possible to formally verify its behavior. If the iterator has a `Finite` instance, consider using `IterM.foldM` instead. -/ @[always_inline, inline] def IterM.Partial.foldM {m : Type w → Type w'} {n : Type w → Type w'} [Monad n] {α : Type w} {β : Type w} {γ : Type w} [Iterator α m β] [IteratorLoopPartial α m n] [MonadLiftT m n] (f : γ → β → n γ) (init : γ) (it : IterM.Partial (α := α) m β) : n γ := ForIn.forIn it init (fun x acc => ForInStep.yield <$> f acc x) /-- Folds a function over an iterator from the left, accumulating a value starting with `init`. The accumulated value is combined with the each element of the list in order, using `f`. It is equivalent to `it.toList.foldl`. This function requires a `Finite` instance proving that the iterator will finish after a finite number of steps. If the iterator is not finite or such an instance is not available, consider using `it.allowNontermination.fold` instead of `it.fold`. However, it is not possible to formally verify the behavior of the partial variant. -/ @[always_inline, inline] def IterM.fold {m : Type w → Type w'} {α : Type w} {β : Type w} {γ : Type w} [Monad m] [Iterator α m β] [Finite α m] [IteratorLoop α m m] (f : γ → β → γ) (init : γ) (it : IterM (α := α) m β) : m γ := ForIn.forIn (m := m) it init (fun x acc => pure (ForInStep.yield (f acc x))) /-- Folds a function over an iterator from the left, accumulating a value starting with `init`. The accumulated value is combined with the each element of the list in order, using `f`. It is equivalent to `it.toList.foldl`. This is a partial, potentially nonterminating, function. It is not possible to formally verify its behavior. If the iterator has a `Finite` instance, consider using `IterM.fold` instead. -/ @[always_inline, inline] def IterM.Partial.fold {m : Type w → Type w'} {α : Type w} {β : Type w} {γ : Type w} [Monad m] [Iterator α m β] [IteratorLoopPartial α m m] (f : γ → β → γ) (init : γ) (it : IterM.Partial (α := α) m β) : m γ := ForIn.forIn (m := m) it init (fun x acc => pure (ForInStep.yield (f acc x))) /-- Iterates over the whole iterator, applying the monadic effects of each step, discarding all emitted values. This function requires a `Finite` instance proving that the iterator will finish after a finite number of steps. If the iterator is not finite or such an instance is not available, consider using `it.allowNontermination.drain` instead of `it.drain`. However, it is not possible to formally verify the behavior of the partial variant. -/ @[always_inline, inline] def IterM.drain {α : Type w} {m : Type w → Type w'} [Monad m] {β : Type w} [Iterator α m β] [Finite α m] (it : IterM (α := α) m β) [IteratorLoop α m m] : m PUnit := it.fold (γ := PUnit) (fun _ _ => .unit) .unit /-- Iterates over the whole iterator, applying the monadic effects of each step, discarding all emitted values. This is a partial, potentially nonterminating, function. It is not possible to formally verify its behavior. If the iterator has a `Finite` instance, consider using `IterM.drain` instead. -/ @[always_inline, inline] def IterM.Partial.drain {α : Type w} {m : Type w → Type w'} [Monad m] {β : Type w} [Iterator α m β] (it : IterM.Partial (α := α) m β) [IteratorLoopPartial α m m] : m PUnit := it.fold (γ := PUnit) (fun _ _ => .unit) .unit set_option doc.verso true in /-- Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for any element emitted by the iterator {name}`it`. {lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are examined in order of iteration. This function requires a {name}`Finite` instance proving that the iterator will finish after a finite number of steps. If the iterator is not finite or such an instance is not available, consider using {lit}`it.allowNontermination.anyM` instead of {lean}`it.anyM`. However, it is not possible to formally verify the behavior of the partial variant. -/ @[specialize] def IterM.anyM {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoop α m m] [Finite α m] (p : β → m (ULift Bool)) (it : IterM (α := α) m β) : m (ULift Bool) := ForIn.forIn it (ULift.up false) (fun x _ => do if (← p x).down then return .done (.up true) else return .yield (.up false)) set_option doc.verso true in /-- Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for any element emitted by the iterator {name}`it`. {lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are examined in order of iteration. This is a partial, potentially nonterminating, function. It is not possible to formally verify its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.anyM` instead. -/ @[specialize] def IterM.Partial.anyM {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoopPartial α m m] (p : β → m (ULift Bool)) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := ForIn.forIn it (ULift.up false) (fun x _ => do if (← p x).down then return .done (.up true) else return .yield (.up false)) set_option doc.verso true in /-- Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for any element emitted by the iterator {name}`it`. {lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are examined in order of iteration. This function requires a {name}`Finite` instance proving that the iterator will finish after a finite number of steps. If the iterator is not finite or such an instance is not available, consider using {lit}`it.allowNontermination.any` instead of {lean}`it.any`. However, it is not possible to formally verify the behavior of the partial variant. -/ @[inline] def IterM.any {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoop α m m] [Finite α m] (p : β → Bool) (it : IterM (α := α) m β) : m (ULift Bool) := do it.anyM (fun x => pure (.up (p x))) set_option doc.verso true in /-- Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for any element emitted by the iterator {name}`it`. {lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are examined in order of iteration. This is a partial, potentially nonterminating, function. It is not possible to formally verify its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.any` instead. -/ @[inline] def IterM.Partial.any {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoopPartial α m m] (p : β → Bool) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do it.anyM (fun x => pure (.up (p x))) set_option doc.verso true in /-- Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for all elements emitted by the iterator {name}`it`. {lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are examined in order of iteration. This function requires a {name}`Finite` instance proving that the iterator will finish after a finite number of steps. If the iterator is not finite or such an instance is not available, consider using {lit}`it.allowNontermination.allM` instead of {lean}`it.allM`. However, it is not possible to formally verify the behavior of the partial variant. -/ @[specialize] def IterM.allM {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoop α m m] [Finite α m] (p : β → m (ULift Bool)) (it : IterM (α := α) m β) : m (ULift Bool) := do ForIn.forIn it (ULift.up true) (fun x _ => do if (← p x).down then return .yield (.up true) else return .done (.up false)) set_option doc.verso true in /-- Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for all elements emitted by the iterator {name}`it`. {lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are examined in order of iteration. This is a partial, potentially nonterminating, function. It is not possible to formally verify its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.allM` instead. -/ @[specialize] def IterM.Partial.allM {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoopPartial α m m] (p : β → m (ULift Bool)) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do ForIn.forIn it (ULift.up true) (fun x _ => do if (← p x).down then return .yield (.up true) else return .done (.up false)) set_option doc.verso true in /-- Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for all elements emitted by the iterator {name}`it`. {lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are examined in order of iteration. This function requires a {name}`Finite` instance proving that the iterator will finish after a finite number of steps. If the iterator is not finite or such an instance is not available, consider using {lit}`it.allowNontermination.all` instead of {lean}`it.all`. However, it is not possible to formally verify the behavior of the partial variant. -/ @[inline] def IterM.all {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoop α m m] [Finite α m] (p : β → Bool) (it : IterM (α := α) m β) : m (ULift Bool) := do it.allM (fun x => pure (.up (p x))) set_option doc.verso true in /-- Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for all elements emitted by the iterator {name}`it`. {lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are examined in order of iteration. This is a partial, potentially nonterminating, function. It is not possible to formally verify its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.all` instead. -/ @[inline] def IterM.Partial.all {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoopPartial α m m] (p : β → Bool) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do it.allM (fun x => pure (.up (p x))) section Size /-- This is the implementation of the default instance `IteratorSize.defaultImplementation`. -/ @[always_inline, inline] def IterM.DefaultConsumers.size {α : Type w} {m : Type w → Type w'} [Monad m] {β : Type w} [Iterator α m β] [IteratorLoop α m m] [Finite α m] (it : IterM (α := α) m β) : m (ULift Nat) := it.fold (init := .up 0) fun acc _ => .up (acc.down + 1) /-- This is the implementation of the default instance `IteratorSizePartial.defaultImplementation`. -/ @[always_inline, inline] def IterM.DefaultConsumers.sizePartial {α : Type w} {m : Type w → Type w'} [Monad m] {β : Type w} [Iterator α m β] [IteratorLoopPartial α m m] (it : IterM (α := α) m β) : m (ULift Nat) := it.allowNontermination.fold (init := .up 0) fun acc _ => .up (acc.down + 1) /-- This is the default implementation of the `IteratorSize` class. It simply iterates using `IteratorLoop` and counts the elements. For certain iterators, more efficient implementations are possible and should be used instead. -/ @[always_inline, inline] def IteratorSize.defaultImplementation {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [Finite α m] [IteratorLoop α m m] : IteratorSize α m where size := IterM.DefaultConsumers.size /-- This is the default implementation of the `IteratorSizePartial` class. It simply iterates using `IteratorLoopPartial` and counts the elements. For certain iterators, more efficient implementations are possible and should be used instead. -/ @[always_inline, inline] instance IteratorSizePartial.defaultImplementation {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [IteratorLoopPartial α m m] : IteratorSizePartial α m where size := IterM.DefaultConsumers.sizePartial /-- Computes how many elements the iterator returns. In monadic situations, it is unclear which effects are caused by calling `size`, and if the monad is nondeterministic, it is also unclear what the returned value should be. The reference implementation, `IteratorSize.defaultImplementation`, simply iterates over the whole iterator monadically, counting the number of emitted values. An `IteratorSize` instance is considered lawful if it is equal to the reference implementation. **Performance**: Default performance is linear in the number of steps taken by the iterator. -/ @[always_inline, inline] def IterM.size {α : Type} {m : Type → Type w'} {β : Type} [Iterator α m β] [Monad m] (it : IterM (α := α) m β) [IteratorSize α m] : m Nat := ULift.down <$> IteratorSize.size it /-- Computes how many elements the iterator emits. With monadic iterators (`IterM`), it is unclear which effects are caused by calling `size`, and if the monad is nondeterministic, it is also unclear what the returned value should be. The reference implementation, `IteratorSize.defaultImplementation`, simply iterates over the whole iterator monadically, counting the number of emitted values. An `IteratorSize` instance is considered lawful if it is equal to the reference implementation. This is the partial version of `size`. It does not require a proof of finiteness and might loop forever. It is not possible to verify the behavior in Lean because it uses `partial`. **Performance**: Default performance is linear in the number of steps taken by the iterator. -/ @[always_inline, inline] def IterM.Partial.size {α : Type} {m : Type → Type w'} {β : Type} [Iterator α m β] [Monad m] (it : IterM.Partial (α := α) m β) [IteratorSizePartial α m] : m Nat := ULift.down <$> IteratorSizePartial.size it.it end Size end Std.Iterators