Skip to content

Commit 83e2262

Browse files
authored
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.
1 parent 9bf5fc2 commit 83e2262

37 files changed

+1242
-499
lines changed

src/Init/Data.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ import Init.Data.RArray
4848
import Init.Data.Vector
4949
import Init.Data.Iterators
5050
import Init.Data.Range.Polymorphic
51+
import Init.Data.Slice

src/Init/Data/Array/Subarray.lean

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,17 @@ module
77

88
prelude
99
import Init.Data.Array.Basic
10+
import Init.Data.Slice.Basic
1011

1112
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
1213
set_option linter.missingDocs true
1314

1415
universe u v w
1516

1617
/--
17-
A region of some underlying array.
18-
19-
A subarray contains an array together with the start and end indices of a region of interest.
20-
Subarrays can be used to avoid copying or allocating space, while being more convenient than
21-
tracking the bounds by hand. The region of interest consists of every index that is both greater
22-
than or equal to `start` and strictly less than `stop`.
18+
Internal representation of `Subarray`, which is an abbreviation for `Slice SubarrayData`.
2319
-/
24-
structure Subarray (α : Type u) where
20+
structure Std.Slice.Internal.SubarrayData (α : Type u) where
2521
/-- The underlying array. -/
2622
array : Array α
2723
/-- The starting index of the region of interest (inclusive). -/
@@ -42,6 +38,40 @@ structure Subarray (α : Type u) where
4238
-/
4339
stop_le_array_size : stop ≤ array.size
4440

41+
open Std.Slice
42+
43+
/--
44+
A region of some underlying array.
45+
46+
A subarray contains an array together with the start and end indices of a region of interest.
47+
Subarrays can be used to avoid copying or allocating space, while being more convenient than
48+
tracking the bounds by hand. The region of interest consists of every index that is both greater
49+
than or equal to `start` and strictly less than `stop`.
50+
-/
51+
abbrev Subarray (α : Type u) := Std.Slice (Internal.SubarrayData α)
52+
53+
instance {α : Type u} : Self (Std.Slice (Internal.SubarrayData α)) (Subarray α) where
54+
55+
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.array]
56+
def Subarray.array (xs : Subarray α) : Array α :=
57+
xs.internalRepresentation.array
58+
59+
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.start]
60+
def Subarray.start (xs : Subarray α) : Nat :=
61+
xs.internalRepresentation.start
62+
63+
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.stop]
64+
def Subarray.stop (xs : Subarray α) : Nat :=
65+
xs.internalRepresentation.stop
66+
67+
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.start_le_stop]
68+
def Subarray.start_le_stop (xs : Subarray α) : xs.start ≤ xs.stop :=
69+
xs.internalRepresentation.start_le_stop
70+
71+
@[always_inline, inline, expose, inherit_doc Internal.SubarrayData.stop_le_array_size]
72+
def Subarray.stop_le_array_size (xs : Subarray α) : xs.stop ≤ xs.array.size :=
73+
xs.internalRepresentation.stop_le_array_size
74+
4575
namespace Subarray
4676

4777
/--
@@ -51,7 +81,7 @@ def size (s : Subarray α) : Nat :=
5181
s.stop - s.start
5282

5383
theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
54-
let {array, start, stop, start_le_stop, stop_le_array_size} := s
84+
let {array, start, stop, start_le_stop, stop_le_array_size} := s
5585
simp [size]
5686
apply Nat.le_trans (Nat.sub_le stop start)
5787
assumption
@@ -102,7 +132,9 @@ Examples:
102132
-/
103133
def popFront (s : Subarray α) : Subarray α :=
104134
if h : s.start < s.stop then
105-
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
135+
⟨{ s.internalRepresentation with
136+
start := s.start + 1,
137+
start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }⟩
106138
else
107139
s
108140

@@ -111,12 +143,13 @@ The empty subarray.
111143
112144
This empty subarray is backed by an empty array.
113145
-/
114-
protected def empty : Subarray α where
115-
array := #[]
116-
start := 0
117-
stop := 0
118-
start_le_stop := Nat.le_refl 0
119-
stop_le_array_size := Nat.le_refl 0
146+
protected def empty : Subarray α := ⟨{
147+
array := #[]
148+
start := 0
149+
stop := 0
150+
start_le_stop := Nat.le_refl 0
151+
stop_le_array_size := Nat.le_refl 0
152+
}⟩
120153

121154
instance : EmptyCollection (Subarray α) :=
122155
⟨Subarray.empty⟩
@@ -410,24 +443,24 @@ Additionally, the starting index is clamped to the ending index.
410443
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
411444
if h₂ : stop ≤ as.size then
412445
if h₁ : start ≤ stop then
413-
{ array := as, start := start, stop := stop,
414-
start_le_stop := h₁, stop_le_array_size := h₂ }
446+
{ array := as, start := start, stop := stop,
447+
start_le_stop := h₁, stop_le_array_size := h₂ }
415448
else
416-
{ array := as, start := stop, stop := stop,
417-
start_le_stop := Nat.le_refl _, stop_le_array_size := h₂ }
449+
{ array := as, start := stop, stop := stop,
450+
start_le_stop := Nat.le_refl _, stop_le_array_size := h₂ }
418451
else
419452
if h₁ : start ≤ as.size then
420-
{ array := as,
421-
start := start,
422-
stop := as.size,
423-
start_le_stop := h₁,
424-
stop_le_array_size := Nat.le_refl _ }
453+
{ array := as,
454+
start := start,
455+
stop := as.size,
456+
start_le_stop := h₁,
457+
stop_le_array_size := Nat.le_refl _ }
425458
else
426-
{ array := as,
427-
start := as.size,
428-
stop := as.size,
429-
start_le_stop := Nat.le_refl _,
430-
stop_le_array_size := Nat.le_refl _ }
459+
{ array := as,
460+
start := as.size,
461+
stop := as.size,
462+
start_le_stop := Nat.le_refl _,
463+
stop_le_array_size := Nat.le_refl _ }
431464

432465
/--
433466
Allocates a new array that contains the contents of the subarray.

src/Init/Data/Array/Subarray/Split.lean

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,44 +21,24 @@ set_option linter.listVariables true -- Enforce naming conventions for `List`/`A
2121
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
2222

2323
namespace Subarray
24-
/--
25-
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
26-
of which contains the remainder.
27-
-/
28-
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
29-
let ⟨i', isLt⟩ := i
30-
have := s.start_le_stop
31-
have := s.stop_le_array_size
32-
have : s.start + i' ≤ s.stop := by
33-
simp only [size] at isLt
34-
omega
35-
let pre := {s with
36-
stop := s.start + i',
37-
start_le_stop := by omega,
38-
stop_le_array_size := by omega
39-
}
40-
let post := {s with
41-
start := s.start + i'
42-
start_le_stop := by assumption
43-
}
44-
(pre, post)
4524

4625
/--
4726
Removes the first `i` elements of the subarray. If there are `i` or fewer elements, the resulting
4827
subarray is empty.
4928
-/
50-
def drop (arr : Subarray α) (i : Nat) : Subarray α where
29+
def drop (arr : Subarray α) (i : Nat) : Subarray α := ⟨{
5130
array := arr.array
5231
start := min (arr.start + i) arr.stop
5332
stop := arr.stop
5433
start_le_stop := by omega
5534
stop_le_array_size := arr.stop_le_array_size
35+
}⟩
5636

5737
/--
5838
Keeps only the first `i` elements of the subarray. If there are `i` or fewer elements, the resulting
5939
subarray is empty.
6040
-/
61-
def take (arr : Subarray α) (i : Nat) : Subarray α where
41+
def take (arr : Subarray α) (i : Nat) : Subarray α := ⟨{
6242
array := arr.array
6343
start := arr.start
6444
stop := min (arr.start + i) arr.stop
@@ -68,3 +48,11 @@ def take (arr : Subarray α) (i : Nat) : Subarray α where
6848
stop_le_array_size := by
6949
have := arr.stop_le_array_size
7050
omega
51+
}⟩
52+
53+
/--
54+
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
55+
of which contains the remainder.
56+
-/
57+
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
58+
(s.take i, s.drop i)

src/Init/Data/Iterators.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ prelude
99
import Init.Data.Iterators.Basic
1010
import Init.Data.Iterators.PostconditionMonad
1111
import Init.Data.Iterators.Consumers
12+
import Init.Data.Iterators.Combinators
1213
import Init.Data.Iterators.Lemmas
14+
import Init.Data.Iterators.ToIterator
1315
import Init.Data.Iterators.Internal
1416

1517
/-!

src/Init/Data/Iterators/Basic.lean

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,20 @@ theorem IterM.IsPlausibleIndirectSuccessorOf.trans {α β : Type w} {m : Type w
401401
case refl => exact h'
402402
case cons_right ih => exact IsPlausibleIndirectSuccessorOf.cons_right ih ‹_›
403403

404+
theorem IterM.IsPlausibleIndirectSuccessorOf.single {α β : Type w} {m : Type w → Type w'}
405+
[Iterator α m β] {it' it : IterM (α := α) m β}
406+
(h : it'.IsPlausibleSuccessorOf it) :
407+
it'.IsPlausibleIndirectSuccessorOf it :=
408+
.cons_right (.refl _) h
409+
410+
theorem IterM.IsPlausibleIndirectOutput.trans {α β : Type w} {m : Type w → Type w'}
411+
[Iterator α m β]
412+
{it' it : IterM (α := α) m β} {out : β} (h : it'.IsPlausibleIndirectSuccessorOf it)
413+
(h' : it'.IsPlausibleIndirectOutput out) : it.IsPlausibleIndirectOutput out := by
414+
induction h
415+
case refl => exact h'
416+
case cons_right ih => exact IsPlausibleIndirectOutput.indirect ‹_› ih
417+
404418
/--
405419
The type of the step object returned by `Iter.step`, containing an `IterStep`
406420
and a proof that this is a plausible step for the given iterator.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
module
7+
8+
prelude
9+
import Init.Data.Iterators.Combinators.Monadic
10+
import Init.Data.Iterators.Combinators.FilterMap
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
module
7+
8+
prelude
9+
import Init.Data.Iterators.Combinators.Monadic.Attach
10+
import Init.Data.Iterators.Combinators.FilterMap
11+
12+
namespace Std.Iterators
13+
14+
@[always_inline, inline, inherit_doc IterM.attachWith]
15+
def Iter.attachWith {α β : Type w}
16+
[Iterator α Id β]
17+
(it : Iter (α := α) β) (P : β → Prop) (h : ∀ out, it.IsPlausibleIndirectOutput out → P out) :
18+
Iter (α := Types.Attach α Id P) { out : β // P out } :=
19+
(it.toIterM.attachWith P ?h).toIter
20+
where finally
21+
case h =>
22+
simp only [← isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM]
23+
exact h
24+
25+
end Std.Iterators

src/Std/Data/Iterators/Combinators/FilterMap.lean renamed to src/Init/Data/Iterators/Combinators/FilterMap.lean

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Paul Reichert
55
-/
6+
module
7+
68
prelude
7-
import Std.Data.Iterators.Combinators.Monadic.FilterMap
9+
import Init.Data.Iterators.Combinators.Monadic.FilterMap
810

911
/-!
1012
@@ -75,7 +77,7 @@ postcondition is `fun x => x.isSome`; if `f` always fails, a suitable postcondit
7577
For each value emitted by the base iterator `it`, this combinator calls `f` and matches on the
7678
returned `Option` value.
7779
-/
78-
@[always_inline, inline]
80+
@[always_inline, inline, expose]
7981
def Iter.filterMapWithPostcondition {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
8082
[Monad m] (f : β → PostconditionT m (Option γ)) (it : Iter (α := α) β) :=
8183
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.filterMapWithPostcondition f : IterM m γ)
@@ -120,7 +122,7 @@ be `fun _ => False`.
120122
121123
For each value emitted by the base iterator `it`, this combinator calls `f`.
122124
-/
123-
@[always_inline, inline]
125+
@[always_inline, inline, expose]
124126
def Iter.filterWithPostcondition {α β : Type w} [Iterator α Id β] {m : Type w → Type w'}
125127
[Monad m] (f : β → PostconditionT m (ULift Bool)) (it : Iter (α := α) β) :=
126128
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.filterWithPostcondition f : IterM m β)
@@ -164,7 +166,7 @@ be `fun _ => False`.
164166
165167
For each value emitted by the base iterator `it`, this combinator calls `f`.
166168
-/
167-
@[always_inline, inline]
169+
@[always_inline, inline, expose]
168170
def Iter.mapWithPostcondition {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
169171
[Monad m] (f : β → PostconditionT m γ) (it : Iter (α := α) β) :=
170172
(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
205207
For each value emitted by the base iterator `it`, this combinator calls `f` and matches on the
206208
returned `Option` value.
207209
-/
208-
@[always_inline, inline]
210+
@[always_inline, inline, expose]
209211
def Iter.filterMapM {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
210212
[Monad m] (f : β → m (Option γ)) (it : Iter (α := α) β) :=
211213
(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
242244
243245
For each value emitted by the base iterator `it`, this combinator calls `f`.
244246
-/
245-
@[always_inline, inline]
247+
@[always_inline, inline, expose]
246248
def Iter.filterM {α β : Type w} [Iterator α Id β] {m : Type w → Type w'}
247249
[Monad m] (f : β → m (ULift Bool)) (it : Iter (α := α) β) :=
248250
(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
281283
282284
For each value emitted by the base iterator `it`, this combinator calls `f`.
283285
-/
284-
@[always_inline, inline]
286+
@[always_inline, inline, expose]
285287
def Iter.mapM {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
286288
[Monad m] (f : β → m γ) (it : Iter (α := α) β) :=
287289
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.mapM f : IterM m γ)
288290

289-
@[always_inline, inline, inherit_doc IterM.filterMap]
291+
@[always_inline, inline, inherit_doc IterM.filterMap, expose]
290292
def Iter.filterMap {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
291293
(f : β → Option γ) (it : Iter (α := α) β) :=
292294
((it.toIterM.filterMap f).toIter : Iter γ)
293295

294-
@[always_inline, inline, inherit_doc IterM.filter]
296+
@[always_inline, inline, inherit_doc IterM.filter, expose]
295297
def Iter.filter {α : Type w} {β : Type w} [Iterator α Id β]
296298
(f : β → Bool) (it : Iter (α := α) β) :=
297299
((it.toIterM.filter f).toIter : Iter β)
298300

299-
@[always_inline, inline, inherit_doc IterM.map]
301+
@[always_inline, inline, inherit_doc IterM.map, expose]
300302
def Iter.map {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
301303
(f : β → γ) (it : Iter (α := α) β) :=
302304
((it.toIterM.map f).toIter : Iter γ)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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+
module
7+
8+
prelude
9+
import Init.Data.Iterators.Combinators.Monadic.FilterMap

0 commit comments

Comments
 (0)