Skip to content

Commit 5c2ca51

Browse files
committed
monadic takeWhile
1 parent 162050b commit 5c2ca51

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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 Init.Data.Nat.Lemmas
8+
import Init.RCases
9+
import Std.Data.Iterators.Basic
10+
import Std.Data.Iterators.Consumers.Monadic.Collect
11+
import Std.Data.Iterators.Consumers.Monadic.Loop
12+
import Std.Data.Iterators.Internal.Termination
13+
import Std.Data.Iterators.PostConditionMonad
14+
15+
/-!
16+
This module provides the iterator combinator `IterM.takeWhile`.
17+
-/
18+
19+
namespace Std.Iterators
20+
21+
variable {α : Type w} {m : Type w → Type w'} {n : Type w → Type w''} {β : Type w}
22+
23+
@[unbox]
24+
structure TakeWhile (α : Type w) (m : Type w → Type w') (β : Type w)
25+
(P : β → PostconditionT m (ULift Bool)) where
26+
inner : IterM (α := α) m β
27+
28+
/--
29+
Given an iterator `it` and a predicate `P`, `it.takeWhile P` is an iterator that outputs
30+
the values emitted by `it` until one of those values violates `P`.
31+
If `P` is violated for some emitted value, the value is dropped and the iterator terminates.
32+
33+
**Marble diagram:**
34+
35+
Assuming that the predicate `P` accepts `a` and `b` but violates `c`:
36+
37+
```text
38+
it ---a----b---c--d-e--⊥
39+
it.takeWhile P ---a----b---⊥
40+
```
41+
42+
**Termination properties:**
43+
44+
* `Finite` instance: only if `it` is finite
45+
* `Productive` instance: only if `it` is productive
46+
47+
Depending on `P`, it is possible `it.takeWhile P` is finite (or productive) although `it` is not.
48+
In this case, the `Finite` (or `Productive`) instance needs to be proved manually.
49+
50+
**Performance:**
51+
52+
This combinator calls `P` on each output of `it` until the predicate evaluates to false. Then
53+
it terminates.
54+
-/
55+
@[inline]
56+
def IterM.takeWhileWithProof (P : β → PostconditionT m (ULift Bool)) (it : IterM (α := α) m β) :=
57+
(toIterM (TakeWhile.mk (P := P) it) m β : IterM m β)
58+
59+
inductive TakeWhile.PlausibleStep [Iterator α m β] {P} (it : IterM (α := TakeWhile α m β P) m β) :
60+
(step : IterStep (IterM (α := TakeWhile α m β P) m β) β) → Prop where
61+
| yield : ∀ {it' out}, it.internalState.inner.IsPlausibleStep (.yield it' out) →
62+
(P out).Property (.up true) → PlausibleStep it (.yield (it'.takeWhileWithProof P) out)
63+
| skip : ∀ {it'}, it.internalState.inner.IsPlausibleStep (.skip it') →
64+
PlausibleStep it (.skip (it'.takeWhileWithProof P))
65+
| done : it.internalState.inner.IsPlausibleStep .done → PlausibleStep it .done
66+
| rejected : ∀ {it' out}, it.internalState.inner.IsPlausibleStep (.yield it' out) →
67+
(P out).Property (.up false) → PlausibleStep it .done
68+
69+
@[always_inline, inline]
70+
instance TakeWhile.instIterator [Monad m] [Iterator α m β] {P} :
71+
Iterator (TakeWhile α m β P) m β where
72+
IsPlausibleStep := TakeWhile.PlausibleStep
73+
step it := do
74+
match ← it.internalState.inner.step with
75+
| .yield it' out h => match ← (P out).operation with
76+
| ⟨.up true, h'⟩ => pure <| .yield (it'.takeWhileWithProof P) out (.yield h h')
77+
| ⟨.up false, h'⟩ => pure <| .done (.rejected h h')
78+
| .skip it' h => pure <| .skip (it'.takeWhileWithProof P) (.skip h)
79+
| .done h => pure <| .done (.done h)
80+
81+
private def TakeWhile.instFinitenessRelation [Monad m] [Iterator α m β]
82+
[Finite α m] {P} :
83+
FinitenessRelation (TakeWhile α m β P) m where
84+
rel := InvImage WellFoundedRelation.rel (IterM.finitelyManySteps ∘ TakeWhile.inner ∘ IterM.internalState)
85+
wf := by
86+
apply InvImage.wf
87+
exact WellFoundedRelation.wf
88+
subrelation {it it'} h := by
89+
obtain ⟨step, h, h'⟩ := h
90+
cases h'
91+
case yield it' out k h' h'' =>
92+
cases h
93+
exact IterM.TerminationMeasures.Finite.rel_of_yield h'
94+
case skip it' out h' =>
95+
cases h
96+
exact IterM.TerminationMeasures.Finite.rel_of_skip h'
97+
case done _ =>
98+
cases h
99+
case rejected _ =>
100+
cases h
101+
102+
instance TakeWhile.instFinite [Monad m] [Iterator α m β] [Finite α m] {P} :
103+
Finite (TakeWhile α m β P) m :=
104+
Finite.of_finitenessRelation instFinitenessRelation
105+
106+
private def TakeWhile.instProductivenessRelation [Monad m] [Iterator α m β]
107+
[Finite α m] {P} :
108+
ProductivenessRelation (TakeWhile α m β P) m where
109+
rel := InvImage WellFoundedRelation.rel (IterM.finitelyManySkips ∘ TakeWhile.inner ∘ IterM.internalState)
110+
wf := by
111+
apply InvImage.wf
112+
exact WellFoundedRelation.wf
113+
subrelation {it it'} h := by
114+
cases h
115+
exact IterM.TerminationMeasures.Productive.rel_of_skip ‹_›
116+
117+
instance TakeWhile.instProductive [Monad m] [Iterator α m β] [Finite α m] {P} :
118+
Productive (TakeWhile α m β P) m :=
119+
Productive.of_productivenessRelation instProductivenessRelation
120+
121+
instance TakeWhile.instIteratorCollect [Monad m] [Monad n] [Iterator α m β] [Productive α m] {P} :
122+
IteratorCollect (TakeWhile α m β P) m n :=
123+
.defaultImplementation
124+
125+
instance TakeWhile.instIteratorCollectPartial [Monad m] [Monad n] [Iterator α m β] {P} :
126+
IteratorCollectPartial (TakeWhile α m β P) m n :=
127+
.defaultImplementation
128+
129+
private def TakeWhile.PlausibleForInStep {β : Type u} {γ : Type v}
130+
(P : β → PostconditionT m (ULift Bool))
131+
(f : β → γ → ForInStep γ → Prop) :
132+
β → γ → (ForInStep γ) → Prop
133+
| out, c, ForInStep.yield c' => (P out).Property (.up true) ∧ f out c (.yield c')
134+
| _, _, .done _ => True
135+
136+
private def TakeWhile.wellFounded_plausibleForInStep {α β : Type w} {m : Type w → Type w'}
137+
[Monad m] [Iterator α m β] {γ : Type x} {P}
138+
{f : β → γ → ForInStep γ → Prop} (wf : IteratorLoop.WellFounded (TakeWhile α m β P) m f) :
139+
IteratorLoop.WellFounded α m (PlausibleForInStep P f) := by
140+
simp only [IteratorLoop.WellFounded] at ⊢ wf
141+
letI : WellFoundedRelation _ := ⟨_, wf⟩
142+
apply Subrelation.wf
143+
(r := InvImage WellFoundedRelation.rel fun p => (p.1.takeWhileWithProof P, p.2))
144+
(fun {p q} h => by
145+
simp only [InvImage, WellFoundedRelation.rel, this, IteratorLoop.rel,
146+
IterM.IsPlausibleStep, Iterator.IsPlausibleStep]
147+
obtain ⟨out, h, h'⟩ | ⟨h, h'⟩ := h
148+
· apply Or.inl
149+
exact ⟨out, .yield h h'.1, h'.2
150+
· apply Or.inr
151+
refine ⟨?_, h'⟩
152+
exact PlausibleStep.skip h)
153+
apply InvImage.wf
154+
exact WellFoundedRelation.wf
155+
156+
instance TakeWhile.instIteratorFor [Monad m] [Monad n] [Iterator α m β]
157+
[IteratorLoop α m n] [MonadLiftT m n] :
158+
IteratorLoop (TakeWhile α m β P) m n where
159+
forIn lift {γ} Plausible wf it init f := by
160+
refine IteratorLoop.forIn lift (γ := γ)
161+
(PlausibleForInStep P Plausible)
162+
(wellFounded_plausibleForInStep wf)
163+
it.internalState.inner
164+
init
165+
fun out acc => do match ← (P out).operation with
166+
| ⟨.up true, h⟩ => match ← f out acc with
167+
| ⟨.yield c, h'⟩ => pure ⟨.yield c, h, h'⟩
168+
| ⟨.done c, h'⟩ => pure ⟨.done c, .intro⟩
169+
| ⟨.up false, h⟩ => pure ⟨.done acc, .intro⟩
170+
171+
instance TakeWhile.instIteratorForPartial [Monad m] [Monad n] [Iterator α m β]
172+
[IteratorLoopPartial α m n] [MonadLiftT m n] {P} :
173+
IteratorLoopPartial (TakeWhile α m β P) m n where
174+
forInPartial lift {γ} it init f := do
175+
IteratorLoopPartial.forInPartial lift it.internalState.inner (γ := γ)
176+
init
177+
fun out acc => do match ← (P out).operation with
178+
| ⟨.up true, _⟩ => match ← f out acc with
179+
| .yield c => pure (.yield c)
180+
| .done c => pure (.done c)
181+
| ⟨.up false, _⟩ => pure (.done acc)
182+
183+
end Std.Iterators

0 commit comments

Comments
 (0)