feat: introduce slices (#8947)

This PR introduces polymorphic slices in their most basic form. They
come with a notation similar to the new range notation. `Subarray` is
now also a slice and can produce an iterator now. It is intended to
migrate more operations of `Subarray` to the `Slice` wrapper type to
make them available for slices of other types, too.

The PR also moves the `filterMap` combinators into `Init` because they
are used internally to implement iterators on array slices.
This commit is contained in:
Paul Reichert 2025-06-26 17:29:03 +02:00 committed by GitHub
parent 9bf5fc2fd3
commit 83e226204d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 1242 additions and 499 deletions

View file

@ -48,3 +48,4 @@ import Init.Data.RArray
import Init.Data.Vector
import Init.Data.Iterators
import Init.Data.Range.Polymorphic
import Init.Data.Slice

View file

@ -7,6 +7,7 @@ module
prelude
import Init.Data.Array.Basic
import Init.Data.Slice.Basic
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
set_option linter.missingDocs true
@ -14,14 +15,9 @@ set_option linter.missingDocs true
universe u v w
/--
A region of some underlying array.
A subarray contains an array together with the start and end indices of a region of interest.
Subarrays can be used to avoid copying or allocating space, while being more convenient than
tracking the bounds by hand. The region of interest consists of every index that is both greater
than or equal to `start` and strictly less than `stop`.
Internal representation of `Subarray`, which is an abbreviation for `Slice SubarrayData`.
-/
structure Subarray (α : Type u) where
structure Std.Slice.Internal.SubarrayData (α : Type u) where
/-- The underlying array. -/
array : Array α
/-- The starting index of the region of interest (inclusive). -/
@ -42,6 +38,40 @@ structure Subarray (α : Type u) where
-/
stop_le_array_size : stop ≤ array.size
open Std.Slice
/--
A region of some underlying array.
A subarray contains an array together with the start and end indices of a region of interest.
Subarrays can be used to avoid copying or allocating space, while being more convenient than
tracking the bounds by hand. The region of interest consists of every index that is both greater
than or equal to `start` and strictly less than `stop`.
-/
abbrev Subarray (α : Type u) := Std.Slice (Internal.SubarrayData α)
instance {α : Type u} : Self (Std.Slice (Internal.SubarrayData α)) (Subarray α) where
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.array]
def Subarray.array (xs : Subarray α) : Array α :=
xs.internalRepresentation.array
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.start]
def Subarray.start (xs : Subarray α) : Nat :=
xs.internalRepresentation.start
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.stop]
def Subarray.stop (xs : Subarray α) : Nat :=
xs.internalRepresentation.stop
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.start_le_stop]
def Subarray.start_le_stop (xs : Subarray α) : xs.start ≤ xs.stop :=
xs.internalRepresentation.start_le_stop
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.stop_le_array_size]
def Subarray.stop_le_array_size (xs : Subarray α) : xs.stop ≤ xs.array.size :=
xs.internalRepresentation.stop_le_array_size
namespace Subarray
/--
@ -51,7 +81,7 @@ def size (s : Subarray α) : Nat :=
s.stop - s.start
theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
let {array, start, stop, start_le_stop, stop_le_array_size} := s
let {array, start, stop, start_le_stop, stop_le_array_size} := s
simp [size]
apply Nat.le_trans (Nat.sub_le stop start)
assumption
@ -102,7 +132,9 @@ Examples:
-/
def popFront (s : Subarray α) : Subarray α :=
if h : s.start < s.stop then
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
⟨{ s.internalRepresentation with
start := s.start + 1,
start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }⟩
else
s
@ -111,12 +143,13 @@ The empty subarray.
This empty subarray is backed by an empty array.
-/
protected def empty : Subarray α where
array := #[]
start := 0
stop := 0
start_le_stop := Nat.le_refl 0
stop_le_array_size := Nat.le_refl 0
protected def empty : Subarray α := ⟨{
array := #[]
start := 0
stop := 0
start_le_stop := Nat.le_refl 0
stop_le_array_size := Nat.le_refl 0
}⟩
instance : EmptyCollection (Subarray α) :=
⟨Subarray.empty⟩
@ -410,24 +443,24 @@ Additionally, the starting index is clamped to the ending index.
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
if h₂ : stop ≤ as.size then
if h₁ : start ≤ stop then
{ array := as, start := start, stop := stop,
start_le_stop := h₁, stop_le_array_size := h₂ }
{ array := as, start := start, stop := stop,
start_le_stop := h₁, stop_le_array_size := h₂ }
else
{ array := as, start := stop, stop := stop,
start_le_stop := Nat.le_refl _, stop_le_array_size := h₂ }
{ array := as, start := stop, stop := stop,
start_le_stop := Nat.le_refl _, stop_le_array_size := h₂ }
else
if h₁ : start ≤ as.size then
{ array := as,
start := start,
stop := as.size,
start_le_stop := h₁,
stop_le_array_size := Nat.le_refl _ }
{ array := as,
start := start,
stop := as.size,
start_le_stop := h₁,
stop_le_array_size := Nat.le_refl _ }
else
{ array := as,
start := as.size,
stop := as.size,
start_le_stop := Nat.le_refl _,
stop_le_array_size := Nat.le_refl _ }
{ array := as,
start := as.size,
stop := as.size,
start_le_stop := Nat.le_refl _,
stop_le_array_size := Nat.le_refl _ }
/--
Allocates a new array that contains the contents of the subarray.

View file

@ -21,44 +21,24 @@ set_option linter.listVariables true -- Enforce naming conventions for `List`/`A
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Subarray
/--
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
of which contains the remainder.
-/
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
let ⟨i', isLt⟩ := i
have := s.start_le_stop
have := s.stop_le_array_size
have : s.start + i' ≤ s.stop := by
simp only [size] at isLt
omega
let pre := {s with
stop := s.start + i',
start_le_stop := by omega,
stop_le_array_size := by omega
}
let post := {s with
start := s.start + i'
start_le_stop := by assumption
}
(pre, post)
/--
Removes the first `i` elements of the subarray. If there are `i` or fewer elements, the resulting
subarray is empty.
-/
def drop (arr : Subarray α) (i : Nat) : Subarray α where
def drop (arr : Subarray α) (i : Nat) : Subarray α := ⟨{
array := arr.array
start := min (arr.start + i) arr.stop
stop := arr.stop
start_le_stop := by omega
stop_le_array_size := arr.stop_le_array_size
}⟩
/--
Keeps only the first `i` elements of the subarray. If there are `i` or fewer elements, the resulting
subarray is empty.
-/
def take (arr : Subarray α) (i : Nat) : Subarray α where
def take (arr : Subarray α) (i : Nat) : Subarray α := ⟨{
array := arr.array
start := arr.start
stop := min (arr.start + i) arr.stop
@ -68,3 +48,11 @@ def take (arr : Subarray α) (i : Nat) : Subarray α where
stop_le_array_size := by
have := arr.stop_le_array_size
omega
}⟩
/--
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
of which contains the remainder.
-/
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
(s.take i, s.drop i)

View file

@ -9,7 +9,9 @@ prelude
import Init.Data.Iterators.Basic
import Init.Data.Iterators.PostconditionMonad
import Init.Data.Iterators.Consumers
import Init.Data.Iterators.Combinators
import Init.Data.Iterators.Lemmas
import Init.Data.Iterators.ToIterator
import Init.Data.Iterators.Internal
/-!

View file

@ -401,6 +401,20 @@ theorem IterM.IsPlausibleIndirectSuccessorOf.trans {α β : Type w} {m : Type w
case refl => exact h'
case cons_right ih => exact IsPlausibleIndirectSuccessorOf.cons_right ih _
theorem IterM.IsPlausibleIndirectSuccessorOf.single {α β : Type w} {m : Type w → Type w'}
[Iterator α m β] {it' it : IterM (α := α) m β}
(h : it'.IsPlausibleSuccessorOf it) :
it'.IsPlausibleIndirectSuccessorOf it :=
.cons_right (.refl _) h
theorem IterM.IsPlausibleIndirectOutput.trans {α β : Type w} {m : Type w → Type w'}
[Iterator α m β]
{it' it : IterM (α := α) m β} {out : β} (h : it'.IsPlausibleIndirectSuccessorOf it)
(h' : it'.IsPlausibleIndirectOutput out) : it.IsPlausibleIndirectOutput out := by
induction h
case refl => exact h'
case cons_right ih => exact IsPlausibleIndirectOutput.indirect _ ih
/--
The type of the step object returned by `Iter.step`, containing an `IterStep`
and a proof that this is a plausible step for the given iterator.

View file

@ -0,0 +1,10 @@
/-
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
import Init.Data.Iterators.Combinators.Monadic
import Init.Data.Iterators.Combinators.FilterMap

View file

@ -0,0 +1,25 @@
/-
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
import Init.Data.Iterators.Combinators.Monadic.Attach
import Init.Data.Iterators.Combinators.FilterMap
namespace Std.Iterators
@[always_inline, inline, inherit_doc IterM.attachWith]
def Iter.attachWith {α β : Type w}
[Iterator α Id β]
(it : Iter (α := α) β) (P : β → Prop) (h : ∀ out, it.IsPlausibleIndirectOutput out → P out) :
Iter (α := Types.Attach α Id P) { out : β // P out } :=
(it.toIterM.attachWith P ?h).toIter
where finally
case h =>
simp only [← isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM]
exact h
end Std.Iterators

View file

@ -3,8 +3,10 @@ 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
import Std.Data.Iterators.Combinators.Monadic.FilterMap
import Init.Data.Iterators.Combinators.Monadic.FilterMap
/-!
@ -75,7 +77,7 @@ postcondition is `fun x => x.isSome`; if `f` always fails, a suitable postcondit
For each value emitted by the base iterator `it`, this combinator calls `f` and matches on the
returned `Option` value.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def Iter.filterMapWithPostcondition {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
[Monad m] (f : β → PostconditionT m (Option γ)) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.filterMapWithPostcondition f : IterM m γ)
@ -120,7 +122,7 @@ be `fun _ => False`.
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def Iter.filterWithPostcondition {α β : Type w} [Iterator α Id β] {m : Type w → Type w'}
[Monad m] (f : β → PostconditionT m (ULift Bool)) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.filterWithPostcondition f : IterM m β)
@ -164,7 +166,7 @@ be `fun _ => False`.
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def Iter.mapWithPostcondition {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
[Monad m] (f : β → PostconditionT m γ) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.mapWithPostcondition f : IterM m γ)
@ -205,7 +207,7 @@ possible to manually prove `Finite` and `Productive` instances depending on the
For each value emitted by the base iterator `it`, this combinator calls `f` and matches on the
returned `Option` value.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def Iter.filterMapM {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
[Monad m] (f : β → m (Option γ)) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.filterMapM f : IterM m γ)
@ -242,7 +244,7 @@ manually prove `Finite` and `Productive` instances depending on the concrete cho
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def Iter.filterM {α β : Type w} [Iterator α Id β] {m : Type w → Type w'}
[Monad m] (f : β → m (ULift Bool)) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.filterM f : IterM m β)
@ -281,22 +283,22 @@ manually prove `Finite` and `Productive` instances depending on the concrete cho
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def Iter.mapM {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
[Monad m] (f : β → m γ) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.mapM f : IterM m γ)
@[always_inline, inline, inherit_doc IterM.filterMap]
@[always_inline, inline, inherit_doc IterM.filterMap, expose]
def Iter.filterMap {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
(f : β → Option γ) (it : Iter (α := α) β) :=
((it.toIterM.filterMap f).toIter : Iter γ)
@[always_inline, inline, inherit_doc IterM.filter]
@[always_inline, inline, inherit_doc IterM.filter, expose]
def Iter.filter {α : Type w} {β : Type w} [Iterator α Id β]
(f : β → Bool) (it : Iter (α := α) β) :=
((it.toIterM.filter f).toIter : Iter β)
@[always_inline, inline, inherit_doc IterM.map]
@[always_inline, inline, inherit_doc IterM.map, expose]
def Iter.map {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
(f : β → γ) (it : Iter (α := α) β) :=
((it.toIterM.map f).toIter : Iter γ)

View file

@ -0,0 +1,9 @@
/-
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
import Init.Data.Iterators.Combinators.Monadic.FilterMap

View file

@ -0,0 +1,136 @@
/-
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
import Init.Data.Iterators.Basic
import Init.Data.Iterators.Internal.Termination
import Init.Data.Iterators.Consumers.Collect
import Init.Data.Iterators.Consumers.Loop
namespace Std.Iterators.Types
/--
Internal state of the `attachWith` combinator. Do not depend on its internals.
-/
@[unbox]
structure Attach (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β]
(P : β → Prop) where
inner : IterM (α := α) m β
invariant : ∀ out, inner.IsPlausibleIndirectOutput out → P out
@[always_inline, inline]
def Attach.modifyStep {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator α m β]
{P : β → Prop}
(it : IterM (α := Attach α m P) m { out : β // P out })
(step : it.internalState.inner.Step (α := α) (m := m)) :
IterStep (IterM (α := Attach α m P) m { out : β // P out })
{ out : β // P out } :=
match step with
| .yield it' out h =>
.yield ⟨it', fun out ho => it.internalState.invariant out (.indirect ⟨_, rfl, h⟩ ho)⟩
⟨out, it.internalState.invariant out (.direct ⟨_, h⟩)⟩
| .skip it' h =>
.skip ⟨it', fun out ho => it.internalState.invariant out (.indirect ⟨_, rfl, h⟩ ho)⟩
| .done _ => .done
instance Attach.instIterator {α β : Type w} {m : Type w → Type w'} [Monad m]
[Iterator α m β] {P : β → Prop} :
Iterator (Attach α m P) m { out : β // P out } where
IsPlausibleStep it step := ∃ step', modifyStep it step' = step
step it := (fun step => ⟨modifyStep it step, step, rfl⟩) <$> it.internalState.inner.step
def Attach.instFinitenessRelation {α β : Type w} {m : Type w → Type w'} [Monad m]
[Iterator α m β] [Finite α m] {P : β → Prop} :
FinitenessRelation (Attach α m P) m where
rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySteps
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
apply Relation.TransGen.single
obtain ⟨_, hs, step, h', rfl⟩ := h
cases step using PlausibleIterStep.casesOn
· simp only [IterStep.successor, modifyStep, Option.some.injEq] at hs
simp only [← hs]
exact ⟨_, rfl, _
· simp only [IterStep.successor, modifyStep, Option.some.injEq] at hs
simp only [← hs]
exact ⟨_, rfl, _
· simp [IterStep.successor, modifyStep, reduceCtorEq] at hs
instance Attach.instFinite {α β : Type w} {m : Type w → Type w'} [Monad m]
[Iterator α m β] [Finite α m] {P : β → Prop} : Finite (Attach α m P) m :=
.of_finitenessRelation instFinitenessRelation
def Attach.instProductivenessRelation {α β : Type w} {m : Type w → Type w'} [Monad m]
[Iterator α m β] [Productive α m] {P : β → Prop} :
ProductivenessRelation (Attach α m P) m where
rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySkips
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
apply Relation.TransGen.single
simp_wf
obtain ⟨step, hs⟩ := h
cases step using PlausibleIterStep.casesOn
· simp [modifyStep] at hs
· simp only [modifyStep, IterStep.skip.injEq] at hs
simp only [← hs]
assumption
· simp [modifyStep] at hs
instance Attach.instProductive {α β : Type w} {m : Type w → Type w'} [Monad m]
[Iterator α m β] [Productive α m] {P : β → Prop} :
Productive (Attach α m P) m :=
.of_productivenessRelation instProductivenessRelation
instance Attach.instIteratorCollect {α β : Type w} {m : Type w → Type w'} [Monad m] [Monad n]
{P : β → Prop} [Iterator α m β] :
IteratorCollect (Attach α m P) m n :=
.defaultImplementation
instance Attach.instIteratorCollectPartial {α β : Type w} {m : Type w → Type w'} [Monad m]
[Monad n] {P : β → Prop} [Iterator α m β] :
IteratorCollectPartial (Attach α m P) m n :=
.defaultImplementation
instance Attach.instIteratorLoop {α β : Type w} {m : Type w → Type w'} [Monad m]
[Monad n] {P : β → Prop} [Iterator α m β] [MonadLiftT m n] :
IteratorLoop (Attach α m P) m n :=
.defaultImplementation
instance Attach.instIteratorLoopPartial {α β : Type w} {m : Type w → Type w'} [Monad m]
[Monad n] {P : β → Prop} [Iterator α m β] [MonadLiftT m n] :
IteratorLoopPartial (Attach α m P) m n :=
.defaultImplementation
instance {α β : Type w} {m : Type w → Type w'} [Monad m]
{P : β → Prop} [Iterator α m β] [IteratorSize α m] :
IteratorSize (Attach α m P) m where
size it := IteratorSize.size it.internalState.inner
instance {α β : Type w} {m : Type w → Type w'} [Monad m]
{P : β → Prop} [Iterator α m β] [IteratorSizePartial α m] :
IteratorSizePartial (Attach α m P) m where
size it := IteratorSizePartial.size it.internalState.inner
end Types
/--
“Attaches” individual proofs to an iterator of values that satisfy a predicate `P`, returning an
iterator with values in the corresponding subtype `{ x // P x }`.
**Termination properties:**
* `Finite` instance: only if the base iterator is finite
* `Productive` instance: only if the base iterator is productive
-/
@[always_inline, inline]
def IterM.attachWith {α β : Type w} {m : Type w → Type w'} [Monad m]
[Iterator α m β] (it : IterM (α := α) m β) (P : β → Prop)
(h : ∀ out, it.IsPlausibleIndirectOutput out → P out) :
IterM (α := Types.Attach α m P) m { out : β // P out } :=
⟨⟨it, h⟩⟩
end Std.Iterators

View file

@ -3,6 +3,8 @@ 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
import Init.Data.Iterators.Basic
import Init.Data.Iterators.Consumers.Collect
@ -45,19 +47,20 @@ structure FilterMap (α : Type w) {β γ : Type w}
/--
Internal state of the `map` combinator. Do not depend on its internals.
-/
@[expose]
def Map (α : Type w) {β γ : Type w} (m : Type w → Type w') (n : Type w → Type w'')
(lift : ⦃α : Type w⦄ → m α → n α) [Functor n]
(f : β → PostconditionT n γ) :=
FilterMap α m n lift (fun b => PostconditionT.map some (f b))
@[always_inline, inline]
@[always_inline, inline, expose]
def IterM.InternalCombinators.filterMap {α β γ : Type w} {m : Type w → Type w'}
{n : Type w → Type w''} (lift : ⦃α : Type w⦄ → m α → n α)
[Iterator α m β] (f : β → PostconditionT n (Option γ))
(it : IterM (α := α) m β) : IterM (α := FilterMap α m n lift f) n γ :=
toIterM ⟨it⟩ n γ
@[always_inline, inline]
@[always_inline, inline, expose]
def IterM.InternalCombinators.map {α β γ : Type w} {m : Type w → Type w'}
{n : Type w → Type w''} [Monad n] (lift : ⦃α : Type w⦄ → m α → n α)
[Iterator α m β] (f : β → PostconditionT n γ)
@ -110,7 +113,7 @@ postcondition is `fun x => x.isSome`; if `f` always fails, a suitable postcondit
For each value emitted by the base iterator `it`, this combinator calls `f` and matches on the
returned `Option` value.
-/
@[inline]
@[inline, expose]
def IterM.filterMapWithPostcondition {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''}
[MonadLiftT m n] [Iterator α m β] (f : β → PostconditionT n (Option γ))
(it : IterM (α := α) m β) : IterM (α := FilterMap α m n (fun ⦃_⦄ => monadLift) f) n γ :=
@ -147,9 +150,9 @@ instance FilterMap.instIterator {α β γ : Type w} {m : Type w → Type w'} {n
match ← it.internalState.inner.step with
| .yield it' out h => do
match ← (f out).operation with
| ⟨none, h'⟩ => pure <| .skip (it'.filterMapWithPostcondition f) (.yieldNone h h')
| ⟨some out', h'⟩ => pure <| .yield (it'.filterMapWithPostcondition f) out' (.yieldSome h h')
| .skip it' h => pure <| .skip (it'.filterMapWithPostcondition f) (.skip h)
| ⟨none, h'⟩ => pure <| .skip (it'.filterMapWithPostcondition f) (by exact .yieldNone h h')
| ⟨some out', h'⟩ => pure <| .yield (it'.filterMapWithPostcondition f) out' (by exact .yieldSome h h')
| .skip it' h => pure <| .skip (it'.filterMapWithPostcondition f) (by exact .skip h)
| .done h => pure <| .done (.done h)
instance {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''} [Monad n] [Iterator α m β]
@ -179,11 +182,13 @@ private def FilterMap.instFinitenessRelation {α β γ : Type w} {m : Type w →
case done h' =>
cases h
@[no_expose]
instance FilterMap.instFinite {α β γ : Type w} {m : Type w → Type w'}
{n : Type w → Type w''} [Monad n] [Iterator α m β] {lift : ⦃α : Type w⦄ → m α → n α}
{f : β → PostconditionT n (Option γ)} [Finite α m] : Finite (FilterMap α m n lift f) n :=
Finite.of_finitenessRelation FilterMap.instFinitenessRelation
@[no_expose]
instance {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''} [Monad n] [Iterator α m β]
{lift : ⦃α : Type w⦄ → m α → n α} {f : β → PostconditionT n γ} [Finite α m] :
Finite (Map α m n lift f) n :=
@ -202,6 +207,7 @@ private def Map.instProductivenessRelation {α β γ : Type w} {m : Type w → T
case skip it' h =>
exact h
@[no_expose]
instance Map.instProductive {α β γ : Type w} {m : Type w → Type w'}
{n : Type w → Type w''} [Monad n] [Iterator α m β] {lift : ⦃α : Type w⦄ → m α → n α}
{f : β → PostconditionT n γ} [Productive α m] :
@ -253,6 +259,7 @@ instance Map.instIteratorCollect {α β γ : Type w} {m : Type w → Type w'}
(fun x => do g (← (f x).operation))
it.internalState.inner (m := m)
@[no_expose]
instance Map.instIteratorCollectPartial {α β γ : Type w} {m : Type w → Type w'}
{n : Type w → Type w''} {o : Type w → Type x} [Monad n] [Monad o] [Iterator α m β]
{lift₁ : ⦃α : Type w⦄ → m α → n α}
@ -318,7 +325,7 @@ be `fun _ => False`.
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline]
@[inline, expose]
def IterM.mapWithPostcondition {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''}
[Monad n] [MonadLiftT m n] [Iterator α m β] (f : β → PostconditionT n γ)
(it : IterM (α := α) m β) : IterM (α := Map α m n (fun ⦃_⦄ => monadLift) f) n γ :=
@ -365,7 +372,7 @@ be `fun _ => False`.
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline]
@[inline, expose]
def IterM.filterWithPostcondition {α β : Type w} {m : Type w → Type w'} {n : Type w → Type w''}
[Monad n] [MonadLiftT m n] [Iterator α m β] (f : β → PostconditionT n (ULift Bool))
(it : IterM (α := α) m β) :=
@ -411,7 +418,7 @@ possible to manually prove `Finite` and `Productive` instances depending on the
For each value emitted by the base iterator `it`, this combinator calls `f` and matches on the
returned `Option` value.
-/
@[inline]
@[inline, expose]
def IterM.filterMapM {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''}
[Iterator α m β] [Monad n] [MonadLiftT m n]
(f : β → n (Option γ)) (it : IterM (α := α) m β) :=
@ -451,7 +458,7 @@ manually prove `Finite` and `Productive` instances depending on the concrete cho
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline]
@[inline, expose]
def IterM.mapM {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''} [Iterator α m β]
[Monad n] [MonadLiftT m n] (f : β → n γ) (it : IterM (α := α) m β) :=
(it.filterMapWithPostcondition (fun b => some <$> PostconditionT.lift (f b)) : IterM n γ)
@ -491,7 +498,7 @@ manually prove `Finite` and `Productive` instances depending on the concrete cho
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline]
@[inline, expose]
def IterM.filterM {α β : Type w} {m : Type w → Type w'} {n : Type w → Type w''} [Iterator α m β]
[Monad n] [MonadLiftT m n] (f : β → n (ULift Bool)) (it : IterM (α := α) m β) :=
(it.filterMapWithPostcondition
@ -528,7 +535,7 @@ be proved manually.
For each value emitted by the base iterator `it`, this combinator calls `f` and matches on the
returned `Option` value.
-/
@[inline]
@[inline, expose]
def IterM.filterMap {α β γ : Type w} {m : Type w → Type w'}
[Iterator α m β] [Monad m] (f : β → Option γ) (it : IterM (α := α) m β) :=
(it.filterMapWithPostcondition (fun b => pure (f b)) : IterM m γ)
@ -557,7 +564,7 @@ it.map ---a'--b'--c'--d'-e'----⊥
For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline]
@[inline, expose]
def IterM.map {α β γ : Type w} {m : Type w → Type w'} [Iterator α m β] [Monad m] (f : β → γ)
(it : IterM (α := α) m β) :=
(it.mapWithPostcondition (fun b => pure (f b)) : IterM m γ)
@ -592,7 +599,7 @@ be proved manually.
For each value emitted by the base iterator `it`, this combinator calls `f` and matches on the
returned value.
-/
@[inline]
@[inline, expose]
def IterM.filter {α β : Type w} {m : Type w → Type w'} [Iterator α m β] [Monad m]
(f : β → Bool) (it : IterM (α := α) m β) :=
(it.filterMap (fun b => if f b then some b else none) : IterM m β)
@ -609,4 +616,18 @@ instance {α β γ : Type w} {m : Type w → Type w'}
IteratorSizePartial (FilterMap α m n lift f) n :=
.defaultImplementation
instance {α β γ : Type w} {m : Type w → Type w'}
{n : Type w → Type w''} [Monad n] [Iterator α m β]
{lift : ⦃α : Type w⦄ → m α → n α}
{f : β → PostconditionT n γ} [IteratorSize α m] :
IteratorSize (Map α m n lift f) n where
size it := lift (IteratorSize.size it.internalState.inner)
instance {α β γ : Type w} {m : Type w → Type w'}
{n : Type w → Type w''} [Monad n] [Iterator α m β]
{lift : ⦃α : Type w⦄ → m α → n α}
{f : β → PostconditionT n γ} [IteratorSizePartial α m] :
IteratorSizePartial (Map α m n lift f) n where
size it := lift (IteratorSizePartial.size it.internalState.inner)
end Std.Iterators

View file

@ -7,3 +7,4 @@ module
prelude
import Init.Data.Iterators.Lemmas.Consumers
import Init.Data.Iterators.Lemmas.Combinators

View file

@ -0,0 +1,10 @@
/-
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
import Init.Data.Iterators.Lemmas.Combinators.Monadic
import Init.Data.Iterators.Lemmas.Combinators.FilterMap

View file

@ -3,10 +3,12 @@ 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
import Init.Data.Iterators.Lemmas.Consumers
import Std.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap
import Std.Data.Iterators.Combinators.FilterMap
import Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap
import Init.Data.Iterators.Combinators.FilterMap
namespace Std.Iterators

View file

@ -0,0 +1,9 @@
/-
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
import Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap

View file

@ -0,0 +1,414 @@
/-
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
import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
import Init.Data.Iterators.Combinators.Monadic.FilterMap
import Init.Data.Iterators.Lemmas.Consumers.Monadic
import all Init.Data.Iterators.Consumers.Monadic.Collect
namespace Std.Iterators
open Std.Internal
section Step
variable {α β β' : Type w} {m : Type w → Type w'} {n : Type w → Type w''}
[Iterator α m β] {it : IterM (α := α) m β}
theorem IterM.step_filterMapWithPostcondition {f : β → PostconditionT n (Option β')}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterMapWithPostcondition f).step = (do
match ← it.step with
| .yield it' out h => do
match ← (f out).operation with
| ⟨none, h'⟩ =>
pure <| .skip (it'.filterMapWithPostcondition f) (.yieldNone (out := out) h h')
| ⟨some out', h'⟩ =>
pure <| .yield (it'.filterMapWithPostcondition f) out' (.yieldSome (out := out) h h')
| .skip it' h =>
pure <| .skip (it'.filterMapWithPostcondition f) (.skip h)
| .done h =>
pure <| .done (by exact .done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PlausibleIterStep.skip, PlausibleIterStep.yield]
apply bind_congr
intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_filterWithPostcondition {f : β → PostconditionT n (ULift Bool)}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterWithPostcondition f).step = (do
match ← it.step with
| .yield it' out h => do
match ← (f out).operation with
| ⟨.up false, h'⟩ =>
pure <| .skip (it'.filterWithPostcondition f) (.yieldNone (out := out) h ⟨⟨_, h'⟩, rfl⟩)
| ⟨.up true, h'⟩ =>
pure <| .yield (it'.filterWithPostcondition f) out (by exact .yieldSome (out := out) h ⟨⟨_, h'⟩, rfl⟩)
| .skip it' h =>
pure <| .skip (it'.filterWithPostcondition f) (by exact .skip h)
| .done h =>
pure <| .done (by exact .done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PostconditionT.operation_map, PlausibleIterStep.skip, PlausibleIterStep.yield,
bind_map_left]
apply bind_congr
intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_mapWithPostcondition {γ : Type w} {f : β → PostconditionT n γ}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.mapWithPostcondition f).step = (do
match ← it.step with
| .yield it' out h => do
let out' ← (f out).operation
pure <| .yield (it'.mapWithPostcondition f) out'.1 (.yieldSome h ⟨out', rfl⟩)
| .skip it' h =>
pure <| .skip (it'.mapWithPostcondition f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PostconditionT.operation_map, bind_map_left, bind_pure_comp]
rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_filterMapM {f : β → n (Option β')}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterMapM f).step = (do
match ← it.step with
| .yield it' out h => do
match ← f out with
| none =>
pure <| .skip (it'.filterMapM f) (.yieldNone (out := out) h .intro)
| some out' =>
pure <| .yield (it'.filterMapM f) out' (.yieldSome (out := out) h .intro)
| .skip it' h =>
pure <| .skip (it'.filterMapM f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PostconditionT.lift, bind_map_left]
apply bind_congr
intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_filterM {f : β → n (ULift Bool)}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterM f).step = (do
match ← it.step with
| .yield it' out h => do
match ← f out with
| .up false =>
pure <| .skip (it'.filterM f) (.yieldNone (out := out) h ⟨⟨.up false, .intro⟩, rfl⟩)
| .up true =>
pure <| .yield (it'.filterM f) out (.yieldSome (out := out) h ⟨⟨.up true, .intro⟩, rfl⟩)
| .skip it' h =>
pure <| .skip (it'.filterM f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PostconditionT.lift, PostconditionT.operation_map, Functor.map_map,
PlausibleIterStep.skip, PlausibleIterStep.yield, bind_map_left]
apply bind_congr
intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_mapM {γ : Type w} {f : β → n γ}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.mapM f).step = (do
match ← it.step with
| .yield it' out h => do
let out' ← f out
pure <| .yield (it'.mapM f) out' (.yieldSome h ⟨⟨out', True.intro⟩, rfl⟩)
| .skip it' h =>
pure <| .skip (it'.mapM f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [bind_pure_comp]
simp only [PostconditionT.lift, Functor.map]
simp only [PostconditionT.operation_map, Functor.map_map, PlausibleIterStep.skip,
PlausibleIterStep.yield, bind_map_left, bind_pure_comp]
rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_filterMap [Monad m] [LawfulMonad m] {f : β → Option β'} :
(it.filterMap f).step = (do
match ← it.step with
| .yield it' out h => do
match h' : f out with
| none =>
pure <| .skip (it'.filterMap f) (.yieldNone h h')
| some out' =>
pure <| .yield (it'.filterMap f) out' (.yieldSome h h')
| .skip it' h =>
pure <| .skip (it'.filterMap f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
simp only [IterM.filterMap, step_filterMapWithPostcondition, pure]
apply bind_congr
intro step
split
· simp only [PostconditionT.pure, PlausibleIterStep.skip, PlausibleIterStep.yield, pure_bind]
split <;> split <;> simp_all
· simp
· simp
theorem IterM.step_map [Monad m] [LawfulMonad m] {f : β → β'} :
(it.map f).step = (do
match ← it.step with
| .yield it' out h =>
let out' := f out
pure <| .yield (it'.map f) out' (.yieldSome h ⟨⟨out', rfl⟩, rfl⟩)
| .skip it' h =>
pure <| .skip (it'.map f) (.skip h)
| .done h => pure <| .done (.done h)) := by
simp only [map, IterM.step_mapWithPostcondition]
apply bind_congr
intro step
split
· simp
· rfl
· rfl
theorem IterM.step_filter [Monad m] [LawfulMonad m] {f : β → Bool} :
(it.filter f).step = (do
match ← it.step with
| .yield it' out h =>
if h' : f out = true then
pure <| .yield (it'.filter f) out (.yieldSome h (by simp [h']))
else
pure <| .skip (it'.filter f) (.yieldNone h (by simp [h']))
| .skip it' h =>
pure <| .skip (it'.filter f) (.skip h)
| .done h => pure <| .done (.done h)) := by
simp only [filter, IterM.step_filterMap]
apply bind_congr
intro step
split
· split
· split
· exfalso; simp_all
· rfl
· split
· congr; simp_all
· exfalso; simp_all
· rfl
· rfl
end Step
section Lawful
@[no_expose]
instance {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''} {o : Type w → Type x}
[Monad m] [Monad n] [Monad o] [LawfulMonad n] [LawfulMonad o] [Iterator α m β] [Finite α m]
[IteratorCollect α m o] [LawfulIteratorCollect α m o]
{lift : ⦃δ : Type w⦄ -> m δ → n δ} {f : β → PostconditionT n γ} [LawfulMonadLiftFunction lift] :
LawfulIteratorCollect (Map α m n lift f) n o where
lawful_toArrayMapped := by
intro δ lift' _ _
letI : MonadLift m n := ⟨lift (δ := _)⟩
letI : MonadLift n o := ⟨lift' (α := _)⟩
ext g it
have : it = IterM.mapWithPostcondition _ it.internalState.inner := by rfl
generalize it.internalState.inner = it at *
cases this
simp only [LawfulIteratorCollect.toArrayMapped_eq]
simp only [IteratorCollect.toArrayMapped]
rw [LawfulIteratorCollect.toArrayMapped_eq]
induction it using IterM.inductSteps with | step it ih_yield ih_skip =>
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp only [bind_assoc]
rw [IterM.step_mapWithPostcondition]
simp only [liftM_bind (m := n) (n := o), bind_assoc]
apply bind_congr
intro step
cases step using PlausibleIterStep.casesOn
· simp only [bind_pure_comp]
simp only [liftM_map, bind_map_left]
apply bind_congr
intro out'
simp only [← ih_yield _]
rfl
· simp only [bind_pure_comp, pure_bind, liftM_pure, pure_bind, ← ih_skip _]
simp only [IterM.mapWithPostcondition, IterM.InternalCombinators.map, internalState_toIterM]
· simp
end Lawful
section ToList
theorem IterM.InternalConsumers.toList_filterMap {α β γ: Type w} {m : Type w → Type w'}
[Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m]
{f : β → Option γ} (it : IterM (α := α) m β) :
(it.filterMap f).toList = (fun x => x.filterMap f) <$> it.toList := by
induction it using IterM.inductSteps
rename_i it ihy ihs
rw [IterM.toList_eq_match_step, IterM.toList_eq_match_step]
simp only [bind_pure_comp, map_bind]
rw [step_filterMap]
simp only [bind_assoc, IterM.step, map_eq_pure_bind]
apply bind_congr
intro step
split
· simp only [List.filterMap_cons, bind_assoc, pure_bind]
split
· split
· simp only [bind_pure_comp, pure_bind]
exact ihy _
· simp_all
· split
· simp_all
· simp_all [ihy _]
· simp only [bind_pure_comp, pure_bind]
apply ihs
assumption
· simp
theorem IterM.toList_filterMap {α β γ : Type w} {m : Type w → Type w'}
[Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m]
{f : β → Option γ} (it : IterM (α := α) m β) :
(it.filterMap f).toList = (fun x => x.filterMap f) <$> it.toList := by
induction it using IterM.inductSteps
rename_i it ihy ihs
rw [IterM.toList_eq_match_step, IterM.toList_eq_match_step]
simp only [bind_pure_comp, map_bind]
rw [step_filterMap]
simp only [bind_assoc, IterM.step, map_eq_pure_bind]
apply bind_congr
intro step
split
· simp only [List.filterMap_cons, bind_assoc, pure_bind]
split
· split
· simp only [bind_pure_comp, pure_bind]
exact ihy _
· simp_all
· split
· simp_all
· simp_all [ihy _]
· simp only [bind_pure_comp, pure_bind]
apply ihs
assumption
· simp
theorem IterM.toList_map {α β β' : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m] {f : β → β'}
(it : IterM (α := α) m β) :
(it.map f).toList = (fun x => x.map f) <$> it.toList := by
rw [LawfulIteratorCollect.toList_eq, ← List.filterMap_eq_map, ← toList_filterMap]
let t := type_of% (it.map f)
let t' := type_of% (it.filterMap (some ∘ f))
congr
· simp [Map]
· simp [instIteratorMap, inferInstanceAs]
congr
simp
· refine heq_of_eqRec_eq ?_ rfl
congr
simp only [Map, PostconditionT.map_pure, Function.comp_apply]
simp only [instIteratorMap, inferInstanceAs, Function.comp_apply]
congr
simp
· simp [Map]
· simp only [instIteratorMap, inferInstanceAs, Function.comp_apply]
congr
simp
· simp only [map, mapWithPostcondition, InternalCombinators.map, Function.comp_apply, filterMap,
filterMapWithPostcondition, InternalCombinators.filterMap]
congr
· simp [Map]
· simp
theorem IterM.toList_filter {α : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
{β : Type w} [Iterator α m β] [Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m]
{f : β → Bool} {it : IterM (α := α) m β} :
(it.filter f).toList = List.filter f <$> it.toList := by
simp only [filter, toList_filterMap, ← List.filterMap_eq_filter]
rfl
end ToList
section ToListRev
theorem IterM.toListRev_filterMap {α β γ : Type w} {m : Type w → Type w'}
[Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m]
{f : β → Option γ} (it : IterM (α := α) m β) :
(it.filterMap f).toListRev = (fun x => x.filterMap f) <$> it.toListRev := by
simp [toListRev_eq, toList_filterMap]
theorem IterM.toListRev_map {α β γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m] {f : β → γ}
(it : IterM (α := α) m β) :
(it.map f).toListRev = (fun x => x.map f) <$> it.toListRev := by
simp [toListRev_eq, toList_map]
theorem IterM.toListRev_filter {α β : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
[Iterator α m β] [Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m]
{f : β → Bool} {it : IterM (α := α) m β} :
(it.filter f).toListRev = List.filter f <$> it.toListRev := by
simp [toListRev_eq, toList_filter]
end ToListRev
section ToArray
theorem IterM.toArray_filterMap {α β γ : Type w} {m : Type w → Type w'}
[Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m]
{f : β → Option γ} (it : IterM (α := α) m β) :
(it.filterMap f).toArray = (fun x => x.filterMap f) <$> it.toArray := by
simp [← toArray_toList, toList_filterMap]
theorem IterM.toArray_map {α β γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m] {f : β → γ}
(it : IterM (α := α) m β) :
(it.map f).toArray = (fun x => x.map f) <$> it.toArray := by
simp [← toArray_toList, toList_map]
theorem IterM.toArray_filter {α : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
{β : Type w} [Iterator α m β] [Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m]
{f : β → Bool} {it : IterM (α := α) m β} :
(it.filter f).toArray = Array.filter f <$> it.toArray := by
simp [← toArray_toList, toList_filter]
end ToArray
end Std.Iterators

View file

@ -45,12 +45,12 @@ Caution: `lift` is not a lawful lift function.
For example, `pure a : PostconditionT m α` is not the same as
`PostconditionT.lift (pure a : m α)`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def PostconditionT.lift {α : Type w} {m : Type w → Type w'} [Functor m] (x : m α) :
PostconditionT m α :=
⟨fun _ => True, (⟨·, .intro⟩) <$> x⟩
@[always_inline, inline]
@[always_inline, inline, expose]
protected def PostconditionT.pure {m : Type w → Type w'} [Pure m] {α : Type w}
(a : α) : PostconditionT m α :=
⟨fun y => a = y, pure <| ⟨a, rfl⟩⟩
@ -70,7 +70,7 @@ turning `PostconditionT m` into a functor.
The postcondition of the `x.map f` states that the return value is the image under `f` of some
`a : α` satisfying the `x.Property`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def PostconditionT.map {m : Type w → Type w'} [Functor m] {α : Type w} {β : Type w}
(f : α → β) (x : PostconditionT m α) : PostconditionT m β :=
⟨fun b => ∃ a : Subtype x.Property, f a.1 = b,

View file

@ -0,0 +1,110 @@
/-
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
import Init.Data.Iterators.Consumers
/-!
This module provides the typeclass `ToIterator`, which is implemented by types that can be
converted into iterators.
-/
open Std.Iterators
namespace Std.Iterators
/--
This typeclass provides an iterator for the given element `x : γ`. Usually, instances are provided
for all elements of a type `γ`.
-/
class ToIterator {γ : Type u} (x : γ) (m : Type w → Type w') (β : outParam (Type w)) where
State : Type w
iterMInternal : IterM (α := State) m β
/-- Converts `x` into a monadic iterator. -/
@[always_inline, inline, expose]
def ToIterator.iterM (x : γ) [ToIterator x m β] : IterM (α := ToIterator.State x m) m β :=
ToIterator.iterMInternal (x := x)
/-- Converts `x` into a pure iterator. -/
@[always_inline, inline, expose]
def ToIterator.iter (x : γ) [ToIterator x Id β] : Iter (α := ToIterator.State x Id) β :=
ToIterator.iterM x |>.toIter
/-- Creates a monadic `ToIterator` instance. -/
@[always_inline, inline, expose]
def ToIterator.ofM {x : γ} (State : Type w)
(iterM : IterM (α := State) m β) :
ToIterator x m β where
State := State
iterMInternal := iterM
/-- Creates a pure `ToIterator` instance. -/
@[always_inline, inline, expose]
def ToIterator.of {x : γ} (State : Type w)
(iter : Iter (α := State) β) :
ToIterator x Id β where
State := State
iterMInternal := iter.toIterM
/-!
## Instance forwarding
If the type defined as `ToIterator.State` implements an iterator typeclass, then this typeclass
should also be available when the type is syntactically visible as `ToIteratorState`. The following
instances are responsible for this forwarding.
-/
instance {x : γ} {State : Type w} {iter}
[Iterator State m β] :
letI i : ToIterator x m β := .ofM State iter
Iterator (α := i.State) m β :=
inferInstanceAs <| Iterator State m β
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [Finite State m] :
letI i : ToIterator x m β := .ofM State iter
Finite (α := i.State) m :=
inferInstanceAs <| Finite (α := State) m
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorCollect State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorCollect (α := i.State) m n :=
inferInstanceAs <| IteratorCollect (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorCollectPartial State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorCollectPartial (α := i.State) m n :=
inferInstanceAs <| IteratorCollectPartial (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorLoop State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorLoop (α := i.State) m n :=
inferInstanceAs <| IteratorLoop (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorLoopPartial State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorLoopPartial (α := i.State) m n :=
inferInstanceAs <| IteratorLoopPartial (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorSize State m] :
letI i : ToIterator x m β := .ofM State iter
IteratorSize (α := i.State) m :=
inferInstanceAs <| IteratorSize (α := State) m
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorSizePartial State m] :
letI i : ToIterator x m β := .ofM State iter
IteratorSizePartial (α := i.State) m :=
inferInstanceAs <| IteratorSizePartial (α := State) m
end Std.Iterators

View file

@ -7,6 +7,7 @@ module
prelude
import Init.Data.Range.Polymorphic.RangeIterator
import Init.Data.Iterators.Combinators.Attach
open Std.Iterators

View file

@ -145,4 +145,86 @@ instance : LawfulRangeSize .open Nat where
Option.some.injEq] at hu h ⊢
omega
instance : ClosedOpenIntersection ⟨.open, .open⟩ Nat where
intersection r s := PRange.mk (max (r.lower + 1) s.lower) (min r.upper s.upper)
example (h : b + 1 ≤ a) : b < a := by omega
instance : LawfulClosedOpenIntersection ⟨.open, .open⟩ Nat where
mem_intersection_iff {a r s} := by
simp only [ClosedOpenIntersection.intersection, Membership.mem, SupportsLowerBound.IsSatisfied,
SupportsUpperBound.IsSatisfied, Nat.max_le, Nat.lt_min, Bound]
omega
instance : ClosedOpenIntersection ⟨.open, .closed⟩ Nat where
intersection r s := PRange.mk (max (r.lower + 1) s.lower) (min (r.upper + 1) s.upper)
instance : LawfulClosedOpenIntersection ⟨.open, .closed⟩ Nat where
mem_intersection_iff {a r s} := by
simp only [ClosedOpenIntersection.intersection, Membership.mem, SupportsLowerBound.IsSatisfied,
SupportsUpperBound.IsSatisfied, Nat.max_le, Nat.lt_min, Bound]
omega
instance : ClosedOpenIntersection ⟨.open, .unbounded⟩ Nat where
intersection r s := PRange.mk (max (r.lower + 1) s.lower) s.upper
instance : LawfulClosedOpenIntersection ⟨.open, .unbounded⟩ Nat where
mem_intersection_iff {a r s} := by
simp only [Membership.mem, SupportsLowerBound.IsSatisfied, Bound,
ClosedOpenIntersection.intersection, Nat.max_le, SupportsUpperBound.IsSatisfied, and_true]
omega
instance : ClosedOpenIntersection ⟨.closed, .open⟩ Nat where
intersection r s := PRange.mk (max r.lower s.lower) (min r.upper s.upper)
instance : LawfulClosedOpenIntersection ⟨.closed, .open⟩ Nat where
mem_intersection_iff {a r s} := by
simp only [ClosedOpenIntersection.intersection, Membership.mem, SupportsLowerBound.IsSatisfied,
SupportsUpperBound.IsSatisfied, Nat.max_le, Nat.lt_min, Bound]
omega
instance : ClosedOpenIntersection ⟨.closed, .closed⟩ Nat where
intersection r s := PRange.mk (max r.lower s.lower) (min (r.upper + 1) s.upper)
instance : LawfulClosedOpenIntersection ⟨.closed, .closed⟩ Nat where
mem_intersection_iff {a r s} := by
simp only [ClosedOpenIntersection.intersection, Membership.mem, SupportsLowerBound.IsSatisfied,
SupportsUpperBound.IsSatisfied, Nat.max_le, Nat.lt_min, Bound]
omega
instance : ClosedOpenIntersection ⟨.closed, .unbounded⟩ Nat where
intersection r s := PRange.mk (max r.lower s.lower) s.upper
instance : LawfulClosedOpenIntersection ⟨.closed, .unbounded⟩ Nat where
mem_intersection_iff {a r s} := by
simp only [Membership.mem, SupportsLowerBound.IsSatisfied, Bound,
ClosedOpenIntersection.intersection, Nat.max_le, SupportsUpperBound.IsSatisfied, and_true]
omega
instance : ClosedOpenIntersection ⟨.unbounded, .open⟩ Nat where
intersection r s := PRange.mk s.lower (min r.upper s.upper)
instance : LawfulClosedOpenIntersection ⟨.unbounded, .open⟩ Nat where
mem_intersection_iff {a r s} := by
simp only [Membership.mem, SupportsLowerBound.IsSatisfied, Bound,
ClosedOpenIntersection.intersection, SupportsUpperBound.IsSatisfied, true_and]
omega
instance : ClosedOpenIntersection ⟨.unbounded, .closed⟩ Nat where
intersection r s := PRange.mk s.lower (min (r.upper + 1) s.upper)
instance : LawfulClosedOpenIntersection ⟨.unbounded, .closed⟩ Nat where
mem_intersection_iff {a r s} := by
simp only [Membership.mem, SupportsLowerBound.IsSatisfied, Bound,
ClosedOpenIntersection.intersection, SupportsUpperBound.IsSatisfied, true_and]
omega
instance : ClosedOpenIntersection ⟨.unbounded, .unbounded⟩ Nat where
intersection _ s := s
instance : LawfulClosedOpenIntersection ⟨.unbounded, .unbounded⟩ Nat where
mem_intersection_iff {a r s} := by
simp [Membership.mem, SupportsLowerBound.IsSatisfied, Bound,
ClosedOpenIntersection.intersection, SupportsUpperBound.IsSatisfied]
end Std.PRange

View file

@ -7,12 +7,8 @@ module
prelude
import Init.Core
import Init.NotationExtra
import Init.Data.Iterators.Consumers
import Init.Data.Range.Polymorphic.UpwardEnumerable
open Std.Iterators
namespace Std.PRange
/--
@ -299,4 +295,30 @@ instance {α} [LT α] [DecidableLT α] [UpwardEnumerable α] [LawfulUpwardEnumer
instance {α} [UpwardEnumerable α] : LawfulUnboundedUpperBound α where
isSatisfied u a := by simp [SupportsUpperBound.IsSatisfied]
/--
This typeclass allows taking the intersection of ranges of the given shape and half-open ranges.
An element should be contained in the intersection if and only if it is contained in both ranges.
This is encoded in `LawfulClosedOpenIntersection`.
-/
class ClosedOpenIntersection (shape : RangeShape) (α : Type w) where
intersection : PRange shape α → PRange ⟨.closed, .open⟩ α → PRange ⟨.closed, .open⟩ α
/--
This typeclass ensures that the intersection according to `ClosedOpenIntersection shape α`
of two ranges contains exactly those elements that are contained in both ranges.
-/
class LawfulClosedOpenIntersection (shape : RangeShape) (α : Type w)
[ClosedOpenIntersection shape α]
[SupportsLowerBound shape.lower α] [SupportsUpperBound shape.upper α]
[SupportsLowerBound .closed α]
[SupportsUpperBound .open α] where
/--
The intersection according to `ClosedOpenIntersection shapee α` of two ranges contains exactly
those elements that are contained in both ranges.
-/
mem_intersection_iff {a : α} {r : PRange ⟨shape.lower, shape.upper⟩ α}
{s : PRange ⟨.closed, .open⟩ α} :
a ∈ ClosedOpenIntersection.intersection r s ↔ a ∈ r ∧ a ∈ s
end Std.PRange

View file

@ -7,6 +7,7 @@ module
prelude
import Init.Data.Iterators.Internal.Termination
import Init.Data.Iterators.Consumers.Access
import Init.Data.Iterators.Consumers.Loop
import Init.Data.Iterators.Consumers.Collect
import Init.Data.Range.Polymorphic.PRange

View file

@ -10,7 +10,6 @@ import Init.Classical
import Init.Core
import Init.Data.Nat.Basic
import Init.Data.Option.Lemmas
import Init.NotationExtra
namespace Std.PRange
@ -67,7 +66,7 @@ The typeclass `Least? α` optionally provides a smallest element of `α`, `least
The main use case of this typeclass is to use it in combination with `UpwardEnumerable` to
obtain a (possibly infinite) ascending enumeration of all elements of `α`.
-/
class Least? (α : Type u) extends UpwardEnumerable α where
class Least? (α : Type u) where
/--
Returns the smallest element of `α`, or none if `α` is empty.

26
src/Init/Data/Slice.lean Normal file
View file

@ -0,0 +1,26 @@
/-
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
import Init.Data.Slice.Basic
import Init.Data.Slice.Notation
import Init.Data.Slice.Operations
import Init.Data.Slice.Array
/-!
# Polymorphic slices
This module provides slices -- views on a subset of all elements of an array or other collection,
demarcated by a range of indices.
* `Init.Data.Slice.Basic` defines the `Slice` structure. All slices are of this type.
* `Init.Data.Slice.Operations` provides functions on `Slice` via dot notation. Many of them are
implemented using iterators under the hood.
* `Init.Data.Slice.Notation` provides slice notation based on ranges, relying on the `Sliceable`
typeclass.
* `Init.Data.Slice.Array` provides the `Sliceable` instance for array slices.
-/

View file

@ -0,0 +1,41 @@
/-
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
import Init.Core
import Init.Data.Array.Subarray
import Init.Data.Iterators.Combinators.Attach
import Init.Data.Iterators.Combinators.FilterMap
import all Init.Data.Range.Polymorphic.Basic
import Init.Data.Range.Polymorphic.Nat
import Init.Data.Slice.Operations
/-!
This module provides slice notation for array slices (a.k.a. `Subarray`) and implements an iterator
for those slices.
-/
open Std Slice PRange Iterators
instance {shape} {α : Type u} [ClosedOpenIntersection shape Nat] :
Sliceable shape (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := ClosedOpenIntersection.intersection range (0)...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
instance {s : Subarray α} : ToIterator s Id α :=
.of _
(PRange.Internal.iter (s.internalRepresentation.start...<s.internalRepresentation.stop)
|>.attachWith (· < s.internalRepresentation.array.size) ?h
|>.map fun i => s.internalRepresentation.array[i.1])
where finally
case h =>
simp only [Internal.isPlausibleIndirectOutput_iter_iff, Membership.mem,
SupportsUpperBound.IsSatisfied, and_imp]
intro out _ h
have := s.internalRepresentation.stop_le_array_size
omega

View file

@ -0,0 +1,35 @@
/-
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
import Init.Core
namespace Std.Slice
/--
Wrapper structure for slice types that makes generic slice functions available via dot notation.
The implementation of the functions depends on the type `γ` of the internal representation.
Usually, if `γ` is the internal representation of a slice of some type `α`, then `Slice γ` can be
used directly, but one usually creates an abbreviation `AlphaSlice := Slice γ` and provides
`Self (Slice γ) AlphaSlice` and `Sliceable shape α AlphaSlice` instances. Then `AlphaSlice` can
be worked with without ever thinking of `Slice` and it is possible to extend the API with
`α`/`γ`-specific functions.
-/
structure _root_.Std.Slice (γ : Type u) where
internalRepresentation : γ
/--
This typeclass determines that some type `α` is equal to `β` and that `β` should be used in APIs
instead of `α`.
`Self` is used in the polymorphic slice library.
-/
class Self (α : Type u) (β : outParam (Type u)) where
eq : α = β := by rfl
end Std.Slice

View file

@ -0,0 +1,44 @@
/-
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
import Init.Data.Range.Polymorphic.PRange
/-!
# Slice notation
This module provides the means to obtain a slice from a collection and a range of indices via
slice notation.
-/
open Std PRange
namespace Std.Slice
/--
This typeclass indicates how to obtain slices of `α` of type `γ`, given ranges of shape `shape` in
the index type `β`.
-/
class Sliceable (shape : RangeShape) (α : Type u) (β : outParam (Type v))
(γ : outParam (Type w)) where
mkSlice (carrier : α) (range : PRange shape β) : γ
macro_rules
| `($c[*...*]) => `(Sliceable.mkSlice $c *...*)
| `($c[$a...*]) => `(Sliceable.mkSlice $c $a...*)
| `($c[$a<...*]) => `(Sliceable.mkSlice $c $a<...*)
| `($c[*...<$b]) => `(Sliceable.mkSlice $c *...<$b)
| `($c[$a...<$b]) => `(Sliceable.mkSlice $c $a...<$b)
| `($c[$a<...<$b]) => `(Sliceable.mkSlice $c $a<...<$b)
| `($c[*...$b]) => `(Sliceable.mkSlice $c *...<$b)
| `($c[$a...$b]) => `(Sliceable.mkSlice $c $a...<$b)
| `($c[$a<...$b]) => `(Sliceable.mkSlice $c $a<...<$b)
| `($c[*...=$b]) => `(Sliceable.mkSlice $c *...=$b)
| `($c[$a...=$b]) => `(Sliceable.mkSlice $c $a...=$b)
| `($c[$a<...=$b]) => `(Sliceable.mkSlice $c $a<...=$b)
end Std.Slice

View file

@ -0,0 +1,57 @@
/-
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
import Init.Data.Slice.Basic
import Init.Data.Slice.Notation
import Init.Data.Iterators
open Std.Iterators
namespace Std.Slice
instance {x : γ} [ToIterator x m β] : ToIterator (Slice.mk x) m β where
State := ToIterator.State x m
iterMInternal := ToIterator.iterMInternal
/--
Internal function to obtain an iterator from a slice. Users should import `Std.Data.Iterators`
and use `Std.Slice.iter` instead.
-/
@[always_inline, inline]
def Internal.iter (s : Slice γ) [ToIterator s Id β] :=
ToIterator.iter s
/--
Returns the number of elements with distinct indices in the given slice.
Example: `#[1, 1, 1][0...2].size = 2`.
-/
@[always_inline, inline]
def size (s : Slice g) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorSize (ToIterator.State s Id) Id] :=
Internal.iter s |>.size
/-- Allocates a new array that contains the elements of the slice. -/
@[always_inline, inline]
def toArray (s : Slice g) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : Array β :=
Internal.iter s |>.toArray
/-- Allocates a new list that contains the elements of the slice. -/
@[always_inline, inline]
def toList (s : Slice g) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : List β :=
Internal.iter s |>.toList
/-- Allocates a new list that contains the elements of the slice in reverse order. -/
@[always_inline, inline]
def toListRev (s : Slice g) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : List β :=
Internal.iter s |>.toListRev
end Std.Slice

View file

@ -97,7 +97,7 @@ instance : Stream (Subarray α) α where
if h : s.start < s.stop then
have : s.start + 1 ≤ s.stop := Nat.succ_le_of_lt h
some (s.array[s.start]'(Nat.lt_of_lt_of_le h s.stop_le_array_size),
{ s with start := s.start + 1, start_le_stop := this })
{ s.internalRepresentation with start := s.start + 1, start_le_stop := this })
else
none

View file

@ -9,6 +9,5 @@ import Std.Data.Iterators.Combinators.Take
import Std.Data.Iterators.Combinators.TakeWhile
import Std.Data.Iterators.Combinators.Drop
import Std.Data.Iterators.Combinators.DropWhile
import Std.Data.Iterators.Combinators.FilterMap
import Std.Data.Iterators.Combinators.StepSize
import Std.Data.Iterators.Combinators.Zip

View file

@ -8,6 +8,5 @@ import Std.Data.Iterators.Combinators.Monadic.Take
import Std.Data.Iterators.Combinators.Monadic.TakeWhile
import Std.Data.Iterators.Combinators.Monadic.Drop
import Std.Data.Iterators.Combinators.Monadic.DropWhile
import Std.Data.Iterators.Combinators.Monadic.FilterMap
import Std.Data.Iterators.Combinators.Monadic.StepSize
import Std.Data.Iterators.Combinators.Monadic.Zip

View file

@ -130,7 +130,7 @@ instance Take.instFinite [Monad m] [Iterator α m β] [Productive α m] :
Finite (Take α m β) m :=
Finite.of_finitenessRelation instFinitenessRelation
instance Take.instIteratorCollect [Monad m] [Monad n] [Iterator α m β] [Productive α m] :
instance Take.instIteratorCollect [Monad m] [Monad n] [Iterator α m β] :
IteratorCollect (Take α m β) m n :=
.defaultImplementation
@ -139,12 +139,12 @@ instance Take.instIteratorCollectPartial [Monad m] [Monad n] [Iterator α m β]
.defaultImplementation
instance Take.instIteratorLoop [Monad m] [Monad n] [Iterator α m β]
[IteratorLoop α m n] [MonadLiftT m n] :
[MonadLiftT m n] :
IteratorLoop (Take α m β) m n :=
.defaultImplementation
instance Take.instIteratorLoopPartial [Monad m] [Monad n] [Iterator α m β]
[IteratorLoopPartial α m n] [MonadLiftT m n] :
[MonadLiftT m n] :
IteratorLoopPartial (Take α m β) m n :=
.defaultImplementation

View file

@ -9,5 +9,4 @@ import Std.Data.Iterators.Lemmas.Combinators.Take
import Std.Data.Iterators.Lemmas.Combinators.TakeWhile
import Std.Data.Iterators.Lemmas.Combinators.Drop
import Std.Data.Iterators.Lemmas.Combinators.DropWhile
import Std.Data.Iterators.Lemmas.Combinators.FilterMap
import Std.Data.Iterators.Lemmas.Combinators.Zip

View file

@ -4,414 +4,12 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
prelude
import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
import Std.Data.Iterators.Combinators.Monadic.FilterMap
import Init.Data.Iterators.Lemmas.Consumers.Monadic
import Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap
import Std.Data.Iterators.Lemmas.Equivalence.StepCongr
namespace Std.Iterators
open Std.Internal
section Step
variable {α β β' : Type w} {m : Type w → Type w'} {n : Type w → Type w''}
[Iterator α m β] {it : IterM (α := α) m β}
theorem IterM.step_filterMapWithPostcondition {f : β → PostconditionT n (Option β')}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterMapWithPostcondition f).step = (do
match ← it.step with
| .yield it' out h => do
match ← (f out).operation with
| ⟨none, h'⟩ =>
pure <| .skip (it'.filterMapWithPostcondition f) (.yieldNone (out := out) h h')
| ⟨some out', h'⟩ =>
pure <| .yield (it'.filterMapWithPostcondition f) out' (.yieldSome (out := out) h h')
| .skip it' h =>
pure <| .skip (it'.filterMapWithPostcondition f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PlausibleIterStep.skip, PlausibleIterStep.yield]
apply bind_congr
intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_filterWithPostcondition {f : β → PostconditionT n (ULift Bool)}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterWithPostcondition f).step = (do
match ← it.step with
| .yield it' out h => do
match ← (f out).operation with
| ⟨.up false, h'⟩ =>
pure <| .skip (it'.filterWithPostcondition f) (.yieldNone (out := out) h ⟨⟨_, h'⟩, rfl⟩)
| ⟨.up true, h'⟩ =>
pure <| .yield (it'.filterWithPostcondition f) out (.yieldSome (out := out) h ⟨⟨_, h'⟩, rfl⟩)
| .skip it' h =>
pure <| .skip (it'.filterWithPostcondition f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PostconditionT.operation_map, PlausibleIterStep.skip, PlausibleIterStep.yield,
bind_map_left]
apply bind_congr
intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_mapWithPostcondition {γ : Type w} {f : β → PostconditionT n γ}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.mapWithPostcondition f).step = (do
match ← it.step with
| .yield it' out h => do
let out' ← (f out).operation
pure <| .yield (it'.mapWithPostcondition f) out'.1 (.yieldSome h ⟨out', rfl⟩)
| .skip it' h =>
pure <| .skip (it'.mapWithPostcondition f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PostconditionT.operation_map, bind_map_left, bind_pure_comp]
rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_filterMapM {f : β → n (Option β')}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterMapM f).step = (do
match ← it.step with
| .yield it' out h => do
match ← f out with
| none =>
pure <| .skip (it'.filterMapM f) (.yieldNone (out := out) h .intro)
| some out' =>
pure <| .yield (it'.filterMapM f) out' (.yieldSome (out := out) h .intro)
| .skip it' h =>
pure <| .skip (it'.filterMapM f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PostconditionT.lift, bind_map_left]
apply bind_congr
intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_filterM {f : β → n (ULift Bool)}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterM f).step = (do
match ← it.step with
| .yield it' out h => do
match ← f out with
| .up false =>
pure <| .skip (it'.filterM f) (.yieldNone (out := out) h ⟨⟨.up false, .intro⟩, rfl⟩)
| .up true =>
pure <| .yield (it'.filterM f) out (.yieldSome (out := out) h ⟨⟨.up true, .intro⟩, rfl⟩)
| .skip it' h =>
pure <| .skip (it'.filterM f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [PostconditionT.lift,
PostconditionT.operation_map, Functor.map_map, PlausibleIterStep.skip,
PlausibleIterStep.yield, bind_map_left]
apply bind_congr
intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_mapM {γ : Type w} {f : β → n γ}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
(it.mapM f).step = (do
match ← it.step with
| .yield it' out h => do
let out' ← f out
pure <| .yield (it'.mapM f) out' (.yieldSome h ⟨⟨out', True.intro⟩, rfl⟩)
| .skip it' h =>
pure <| .skip (it'.mapM f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
apply bind_congr
intro step
match step with
| .yield it' out h =>
simp only [bind_pure_comp]
simp only [PostconditionT.lift, Functor.map,
]
simp only [PostconditionT.operation_map, Functor.map_map, PlausibleIterStep.skip,
PlausibleIterStep.yield, bind_map_left, bind_pure_comp]
rfl
| .skip it' h => rfl
| .done h => rfl
theorem IterM.step_filterMap [Monad m] [LawfulMonad m] {f : β → Option β'} :
(it.filterMap f).step = (do
match ← it.step with
| .yield it' out h => do
match h' : f out with
| none =>
pure <| .skip (it'.filterMap f) (.yieldNone h h')
| some out' =>
pure <| .yield (it'.filterMap f) out' (.yieldSome h h')
| .skip it' h =>
pure <| .skip (it'.filterMap f) (.skip h)
| .done h =>
pure <| .done (.done h)) := by
simp only [IterM.filterMap, step_filterMapWithPostcondition, pure]
apply bind_congr
intro step
split
· simp only [PostconditionT.pure, PlausibleIterStep.skip, PlausibleIterStep.yield, pure_bind]
split <;> split <;> simp_all
· simp
· simp
theorem IterM.step_map [Monad m] [LawfulMonad m] {f : β → β'} :
(it.map f).step = (do
match ← it.step with
| .yield it' out h =>
let out' := f out
pure <| .yield (it'.map f) out' (.yieldSome h ⟨⟨out', rfl⟩, rfl⟩)
| .skip it' h =>
pure <| .skip (it'.map f) (.skip h)
| .done h => pure <| .done (.done h)) := by
simp only [map, IterM.step_mapWithPostcondition]
apply bind_congr
intro step
split
· simp
· rfl
· rfl
theorem IterM.step_filter [Monad m] [LawfulMonad m] {f : β → Bool} :
(it.filter f).step = (do
match ← it.step with
| .yield it' out h =>
if h' : f out = true then
pure <| .yield (it'.filter f) out (.yieldSome h (by simp [h']))
else
pure <| .skip (it'.filter f) (.yieldNone h (by simp [h']))
| .skip it' h =>
pure <| .skip (it'.filter f) (.skip h)
| .done h => pure <| .done (.done h)) := by
simp only [filter, IterM.step_filterMap]
apply bind_congr
intro step
split
· split
· split
· exfalso; simp_all
· rfl
· split
· congr; simp_all
· exfalso; simp_all
· rfl
· rfl
end Step
section Lawful
instance {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''} {o : Type w → Type x}
[Monad m] [Monad n] [Monad o] [LawfulMonad n] [LawfulMonad o] [Iterator α m β] [Finite α m]
[IteratorCollect α m o] [LawfulIteratorCollect α m o]
{lift : ⦃δ : Type w⦄ -> m δ → n δ} {f : β → PostconditionT n γ} [LawfulMonadLiftFunction lift] :
LawfulIteratorCollect (Map α m n lift f) n o where
lawful_toArrayMapped := by
intro δ lift' _ _
letI : MonadLift m n := ⟨lift (δ := _)⟩
letI : MonadLift n o := ⟨lift' (α := _)⟩
ext g it
have : it = IterM.mapWithPostcondition _ it.internalState.inner := by rfl
generalize it.internalState.inner = it at *
cases this
simp only [LawfulIteratorCollect.toArrayMapped_eq]
simp only [IteratorCollect.toArrayMapped]
rw [LawfulIteratorCollect.toArrayMapped_eq]
induction it using IterM.inductSteps with | step it ih_yield ih_skip =>
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp only [bind_assoc]
rw [IterM.step_mapWithPostcondition]
simp only [liftM_bind (m := n) (n := o), bind_assoc]
apply bind_congr
intro step
cases step using PlausibleIterStep.casesOn
· simp only [bind_pure_comp]
simp only [liftM_map, bind_map_left]
apply bind_congr
intro out'
simp only [← ih_yield _]
rfl
· simp only [bind_pure_comp, pure_bind, liftM_pure, pure_bind, ← ih_skip _]
simp only [IterM.mapWithPostcondition, IterM.InternalCombinators.map, internalState_toIterM]
· simp
end Lawful
section ToList
theorem IterM.InternalConsumers.toList_filterMap {α β γ: Type w} {m : Type w → Type w'}
[Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m]
{f : β → Option γ} (it : IterM (α := α) m β) :
(it.filterMap f).toList = (fun x => x.filterMap f) <$> it.toList := by
induction it using IterM.inductSteps
rename_i it ihy ihs
rw [IterM.toList_eq_match_step, IterM.toList_eq_match_step]
simp only [bind_pure_comp, map_bind]
rw [step_filterMap]
simp only [bind_assoc, IterM.step, map_eq_pure_bind]
apply bind_congr
intro step
split
· simp only [List.filterMap_cons, bind_assoc, pure_bind]
split
· split
· simp only [bind_pure_comp, pure_bind]
exact ihy _
· simp_all
· split
· simp_all
· simp_all [ihy _]
· simp only [bind_pure_comp, pure_bind]
apply ihs
assumption
· simp
theorem IterM.toList_filterMap {α β γ : Type w} {m : Type w → Type w'}
[Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m]
{f : β → Option γ} (it : IterM (α := α) m β) :
(it.filterMap f).toList = (fun x => x.filterMap f) <$> it.toList := by
induction it using IterM.inductSteps
rename_i it ihy ihs
rw [IterM.toList_eq_match_step, IterM.toList_eq_match_step]
simp only [bind_pure_comp, map_bind]
rw [step_filterMap]
simp only [bind_assoc, IterM.step, map_eq_pure_bind]
apply bind_congr
intro step
split
· simp only [List.filterMap_cons, bind_assoc, pure_bind]
split
· split
· simp only [bind_pure_comp, pure_bind]
exact ihy _
· simp_all
· split
· simp_all
· simp_all [ihy _]
· simp only [bind_pure_comp, pure_bind]
apply ihs
assumption
· simp
theorem IterM.toList_map {α β β' : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m] {f : β → β'}
(it : IterM (α := α) m β) :
(it.map f).toList = (fun x => x.map f) <$> it.toList := by
rw [LawfulIteratorCollect.toList_eq, ← List.filterMap_eq_map, ← toList_filterMap]
let t := type_of% (it.map f)
let t' := type_of% (it.filterMap (some ∘ f))
congr
· simp [Map]
· simp [instIteratorMap, inferInstanceAs]
congr
simp
· refine heq_of_eqRec_eq ?_ rfl
congr
simp only [Map, PostconditionT.map_pure, Function.comp_apply]
simp only [instIteratorMap, inferInstanceAs, Function.comp_apply]
congr
simp
· simp [Map]
· simp only [instIteratorMap, inferInstanceAs, Function.comp_apply]
congr
simp
· simp only [map, mapWithPostcondition, InternalCombinators.map, Function.comp_apply, filterMap,
filterMapWithPostcondition, InternalCombinators.filterMap]
congr
· simp [Map]
· simp
theorem IterM.toList_filter {α : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
{β : Type w} [Iterator α m β] [Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m]
{f : β → Bool} {it : IterM (α := α) m β} :
(it.filter f).toList = List.filter f <$> it.toList := by
simp only [filter, toList_filterMap, ← List.filterMap_eq_filter]
rfl
end ToList
section ToListRev
theorem IterM.toListRev_filterMap {α β γ : Type w} {m : Type w → Type w'}
[Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m]
{f : β → Option γ} (it : IterM (α := α) m β) :
(it.filterMap f).toListRev = (fun x => x.filterMap f) <$> it.toListRev := by
simp [toListRev_eq, toList_filterMap]
theorem IterM.toListRev_map {α β γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m] {f : β → γ}
(it : IterM (α := α) m β) :
(it.map f).toListRev = (fun x => x.map f) <$> it.toListRev := by
simp [toListRev_eq, toList_map]
theorem IterM.toListRev_filter {α β : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
[Iterator α m β] [Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m]
{f : β → Bool} {it : IterM (α := α) m β} :
(it.filter f).toListRev = List.filter f <$> it.toListRev := by
simp [toListRev_eq, toList_filter]
end ToListRev
section ToArray
theorem IterM.toArray_filterMap {α β γ : Type w} {m : Type w → Type w'}
[Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m]
{f : β → Option γ} (it : IterM (α := α) m β) :
(it.filterMap f).toArray = (fun x => x.filterMap f) <$> it.toArray := by
simp [← toArray_toList, toList_filterMap]
theorem IterM.toArray_map {α β γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
[Iterator α m β] [IteratorCollect α m m] [LawfulIteratorCollect α m m] [Finite α m] {f : β → γ}
(it : IterM (α := α) m β) :
(it.map f).toArray = (fun x => x.map f) <$> it.toArray := by
simp [← toArray_toList, toList_map]
theorem IterM.toArray_filter {α : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
{β : Type w} [Iterator α m β] [Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m]
{f : β → Bool} {it : IterM (α := α) m β} :
(it.filter f).toArray = Array.filter f <$> it.toArray := by
simp [← toArray_toList, toList_filter]
end ToArray
section Equivalence
theorem stepAsHetT_filterMapWithPostcondition [Monad m] [LawfulMonad m] [Monad n]
[LawfulMonad n] [Iterator α m β] [MonadLiftT m n] [LawfulMonadLiftT m n]
{f : β → PostconditionT n (Option γ)} {it : IterM (α := α) m β} :
@ -599,6 +197,4 @@ theorem IterM.Equiv.map {α₁ α₂ β γ : Type w}
IterM.Equiv (ita.map f) (itb.map f) :=
IterM.Equiv.filterMapWithPostcondition h
end Equivalence
end Std.Iterators

View file

@ -10,3 +10,4 @@ import Std.Data.Iterators.Producers.Empty
import Std.Data.Iterators.Producers.List
import Std.Data.Iterators.Producers.Range
import Std.Data.Iterators.Producers.Repeat
import Std.Data.Iterators.Producers.Slice

View file

@ -0,0 +1,23 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
prelude
import Init.Data.Slice.Operations
/-!
# Slice iterator
This module provides iterators over slices from `Std.Slice` via `Std.Slice.iter`.
-/
open Std Slice Iterators
/--
Returns an iterator over the given slice. This iterator will emit the elements of the slice
in increasing order of the indices.
-/
@[always_inline, inline]
def Std.Slice.iter (s : Slice γ) [ToIterator s Id β] :=
(Internal.iter s : Iter β)

31
tests/lean/run/slice.lean Normal file
View file

@ -0,0 +1,31 @@
import Std.Data.Iterators
example : #[1, 2, 3][*...*].toList = [1, 2, 3] := by native_decide
example : #[1, 2, 3][*...2].toList = [1, 2] := by native_decide
example : #[1, 2, 3][*...<2].toList = [1, 2] := by native_decide
example : #[1, 2, 3][*...=1].toList = [1, 2] := by native_decide
example : #[1, 2, 3][0<...*].toList = [2, 3] := by native_decide
example : #[1, 2, 3][0<...2].toList = [2] := by native_decide
example : #[1, 2, 3][0<...<2].toList = [2] := by native_decide
example : #[1, 2, 3][0<...=1].toList = [2] := by native_decide
example : #[1, 2, 3][1...*].toList = [2, 3] := by native_decide
example : #[1, 2, 3][1...2].toList = [2] := by native_decide
example : #[1, 2, 3][1...<2].toList = [2] := by native_decide
example : #[1, 2, 3][1...=1].toList = [2] := by native_decide
example : #[1, 2, 3][1...<10].size = 2 := by native_decide
example : (#[1, 2, 3][1...*].take 1).toList = [2] := by native_decide
example : #[1, 1, 1][0...2].size = 2 := by native_decide