Skip to content

Commit a73ebe8

Browse files
authored
feat: any/all predicates for iterators (#10686)
This PR introduces `any`, `anyM`, `all` and `allM` for pure and monadic iterators. It also provides lemmas about them.
1 parent 3931a72 commit a73ebe8

File tree

9 files changed

+1372
-4
lines changed

9 files changed

+1372
-4
lines changed

src/Init/Control/Lawful/Basic.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ instance : LawfulMonad Id := by
252252
@[simp] theorem run_map (x : Id α) (f : α → β) : (f <$> x).run = f x.run := rfl
253253
@[simp] theorem run_bind (x : Id α) (f : α → Id β) : (x >>= f).run = (f x.run).run := rfl
254254
@[simp] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
255+
@[simp] theorem pure_run (a : Id α) : pure a.run = a := rfl
255256
@[simp] theorem run_seqRight (x y : Id α) : (x *> y).run = y.run := rfl
256257
@[simp] theorem run_seqLeft (x y : Id α) : (x <* y).run = x.run := rfl
257258
@[simp] theorem run_seq (f : Id (α → β)) (x : Id α) : (f <*> x).run = f.run x.run := rfl

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
463463
@[inline, expose]
464464
def IterM.mapM {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''} [Iterator α m β]
465465
[Monad n] [MonadLiftT m n] (f : β → n γ) (it : IterM (α := α) m β) :=
466-
(it.filterMapWithPostcondition (fun b => some <$> PostconditionT.lift (f b)) : IterM n γ)
466+
(it.mapWithPostcondition (fun b => PostconditionT.lift (f b)) : IterM n γ)
467467

468468
/--
469469
If `it` is an iterator, then `it.filterM f` is another iterator that applies a monadic

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,70 @@ def Iter.Partial.fold {α : Type w} {β : Type w} {γ : Type x} [Iterator α Id
139139
(init : γ) (it : Iter.Partial (α := α) β) : γ :=
140140
ForIn.forIn (m := Id) it init (fun x acc => ForInStep.yield (f acc x))
141141

142+
set_option doc.verso true in
143+
/--
144+
Returns {lean}`true` if the monadic predicate {name}`p` returns {lean}`true` for
145+
any element emitted by the iterator {name}`it`.
146+
147+
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
148+
examined in order of iteration.
149+
-/
150+
@[specialize]
151+
def Iter.anyM {α β : Type w} {m : TypeType w'} [Monad m]
152+
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
153+
(p : β → m Bool) (it : Iter (α := α) β) : m Bool :=
154+
ForIn.forIn it false (fun x _ => do
155+
if ← p x then
156+
return .done true
157+
else
158+
return .yield false)
159+
160+
set_option doc.verso true in
161+
/--
162+
Returns {lean}`true` if the pure predicate {name}`p` returns {lean}`true` for
163+
any element emitted by the iterator {name}`it`.
164+
165+
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
166+
examined in order of iteration.
167+
-/
168+
@[inline]
169+
def Iter.any {α β : Type w}
170+
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
171+
(p : β → Bool) (it : Iter (α := α) β) : Bool :=
172+
(it.anyM (fun x => pure (f := Id) (p x))).run
173+
174+
set_option doc.verso true in
175+
/--
176+
Returns {lean}`true` if the monadic predicate {name}`p` returns {lean}`true` for
177+
all elements emitted by the iterator {name}`it`.
178+
179+
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
180+
examined in order of iteration.
181+
-/
182+
@[specialize]
183+
def Iter.allM {α β : Type w} {m : TypeType w'} [Monad m]
184+
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
185+
(p : β → m Bool) (it : Iter (α := α) β) : m Bool :=
186+
ForIn.forIn it true (fun x _ => do
187+
if ← p x then
188+
return .yield true
189+
else
190+
return .done false)
191+
192+
set_option doc.verso true in
193+
/--
194+
Returns {lean}`true` if the pure predicate {name}`p` returns {lean}`true` for
195+
all elements emitted by the iterator {name}`it`.
196+
197+
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
198+
examined in order of iteration.
199+
-/
200+
@[inline]
201+
def Iter.all {α β : Type w}
202+
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
203+
(p : β → Bool) (it : Iter (α := α) β) : Bool :=
204+
(it.allM (fun x => pure (f := Id) (p x))).run
205+
142206
@[always_inline, inline, expose, inherit_doc IterM.size]
143207
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorSize α Id]
144208
(it : Iter (α := α) β) : Nat :=

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

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,169 @@ def IterM.Partial.drain {α : Type w} {m : Type w → Type w'} [Monad m] {β : T
416416
m PUnit :=
417417
it.fold (γ := PUnit) (fun _ _ => .unit) .unit
418418

419+
set_option doc.verso true in
420+
/--
421+
Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for
422+
any element emitted by the iterator {name}`it`.
423+
424+
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
425+
examined in order of iteration.
426+
427+
This function requires a {name}`Finite` instance proving that the iterator will finish after a
428+
finite number of steps. If the iterator is not finite or such an instance is not available,
429+
consider using {lit}`it.allowNontermination.anyM` instead of {lean}`it.anyM`. However, it is not
430+
possible to formally verify the behavior of the partial variant.
431+
-/
432+
@[specialize]
433+
def IterM.anyM {α β : Type w} {m : Type w → Type w'} [Monad m]
434+
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
435+
(p : β → m (ULift Bool)) (it : IterM (α := α) m β) : m (ULift Bool) :=
436+
ForIn.forIn it (ULift.up false) (fun x _ => do
437+
if (← p x).down then
438+
return .done (.up true)
439+
else
440+
return .yield (.up false))
441+
442+
set_option doc.verso true in
443+
/--
444+
Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for
445+
any element emitted by the iterator {name}`it`.
446+
447+
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
448+
examined in order of iteration.
449+
450+
This is a partial, potentially nonterminating, function. It is not possible to formally verify
451+
its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.anyM`
452+
instead.
453+
-/
454+
@[specialize]
455+
def IterM.Partial.anyM {α β : Type w} {m : Type w → Type w'} [Monad m]
456+
[Iterator α m β] [IteratorLoopPartial α m m]
457+
(p : β → m (ULift Bool)) (it : IterM.Partial (α := α) m β) : m (ULift Bool) :=
458+
ForIn.forIn it (ULift.up false) (fun x _ => do
459+
if (← p x).down then
460+
return .done (.up true)
461+
else
462+
return .yield (.up false))
463+
464+
set_option doc.verso true in
465+
/--
466+
Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for
467+
any element emitted by the iterator {name}`it`.
468+
469+
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
470+
examined in order of iteration.
471+
472+
This function requires a {name}`Finite` instance proving that the iterator will finish after a
473+
finite number of steps. If the iterator is not finite or such an instance is not available,
474+
consider using {lit}`it.allowNontermination.any` instead of {lean}`it.any`. However, it is not
475+
possible to formally verify the behavior of the partial variant.
476+
-/
477+
@[inline]
478+
def IterM.any {α β : Type w} {m : Type w → Type w'} [Monad m]
479+
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
480+
(p : β → Bool) (it : IterM (α := α) m β) : m (ULift Bool) := do
481+
it.anyM (fun x => pure (.up (p x)))
482+
483+
set_option doc.verso true in
484+
/--
485+
Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for
486+
any element emitted by the iterator {name}`it`.
487+
488+
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
489+
examined in order of iteration.
490+
491+
This is a partial, potentially nonterminating, function. It is not possible to formally verify
492+
its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.any`
493+
instead.
494+
-/
495+
@[inline]
496+
def IterM.Partial.any {α β : Type w} {m : Type w → Type w'} [Monad m]
497+
[Iterator α m β] [IteratorLoopPartial α m m]
498+
(p : β → Bool) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do
499+
it.anyM (fun x => pure (.up (p x)))
500+
501+
set_option doc.verso true in
502+
/--
503+
Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for
504+
all elements emitted by the iterator {name}`it`.
505+
506+
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
507+
examined in order of iteration.
508+
509+
This function requires a {name}`Finite` instance proving that the iterator will finish after a
510+
finite number of steps. If the iterator is not finite or such an instance is not available,
511+
consider using {lit}`it.allowNontermination.allM` instead of {lean}`it.allM`. However, it is not
512+
possible to formally verify the behavior of the partial variant.
513+
-/
514+
@[specialize]
515+
def IterM.allM {α β : Type w} {m : Type w → Type w'} [Monad m]
516+
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
517+
(p : β → m (ULift Bool)) (it : IterM (α := α) m β) : m (ULift Bool) := do
518+
ForIn.forIn it (ULift.up true) (fun x _ => do
519+
if (← p x).down then
520+
return .yield (.up true)
521+
else
522+
return .done (.up false))
523+
set_option doc.verso true in
524+
/--
525+
Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for
526+
all elements emitted by the iterator {name}`it`.
527+
528+
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
529+
examined in order of iteration.
530+
531+
This is a partial, potentially nonterminating, function. It is not possible to formally verify
532+
its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.allM`
533+
instead.
534+
-/
535+
@[specialize]
536+
def IterM.Partial.allM {α β : Type w} {m : Type w → Type w'} [Monad m]
537+
[Iterator α m β] [IteratorLoopPartial α m m]
538+
(p : β → m (ULift Bool)) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do
539+
ForIn.forIn it (ULift.up true) (fun x _ => do
540+
if (← p x).down then
541+
return .yield (.up true)
542+
else
543+
return .done (.up false))
544+
545+
set_option doc.verso true in
546+
/--
547+
Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for
548+
all elements emitted by the iterator {name}`it`.
549+
550+
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
551+
examined in order of iteration.
552+
553+
This function requires a {name}`Finite` instance proving that the iterator will finish after a
554+
finite number of steps. If the iterator is not finite or such an instance is not available,
555+
consider using {lit}`it.allowNontermination.all` instead of {lean}`it.all`. However, it is not
556+
possible to formally verify the behavior of the partial variant.
557+
-/
558+
@[inline]
559+
def IterM.all {α β : Type w} {m : Type w → Type w'} [Monad m]
560+
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
561+
(p : β → Bool) (it : IterM (α := α) m β) : m (ULift Bool) := do
562+
it.allM (fun x => pure (.up (p x)))
563+
564+
set_option doc.verso true in
565+
/--
566+
Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for
567+
all elements emitted by the iterator {name}`it`.
568+
569+
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
570+
examined in order of iteration.
571+
572+
This is a partial, potentially nonterminating, function. It is not possible to formally verify
573+
its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.all`
574+
instead.
575+
-/
576+
@[inline]
577+
def IterM.Partial.all {α β : Type w} {m : Type w → Type w'} [Monad m]
578+
[Iterator α m β] [IteratorLoopPartial α m m]
579+
(p : β → Bool) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do
580+
it.allM (fun x => pure (.up (p x)))
581+
419582
section Size
420583

421584
/--

0 commit comments

Comments
 (0)