Skip to content

Commit b6c851c

Browse files
authored
Merge branch 'main' into repo-assist/improve-splitat-cc1b1026bf3ab37b
2 parents 30e91bb + efa9e4c commit b6c851c

8 files changed

Lines changed: 504 additions & 150 deletions

File tree

.github/aw/actions-lock.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
"version": "v8",
1111
"sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd"
1212
},
13-
"github/gh-aw/actions/setup@v0.52.1": {
13+
"github/gh-aw/actions/setup@v0.53.6": {
1414
"repo": "github/gh-aw/actions/setup",
15-
"version": "v0.52.1",
16-
"sha": "a86e657586e4ac5f549a790628971ec02f6a4a8f"
15+
"version": "v0.53.6",
16+
"sha": "956f874e40e831c08a8b01ec76f5d49ae3fe8387"
1717
}
1818
}
1919
}

.github/workflows/repo-assist.lock.yml

Lines changed: 185 additions & 78 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/repo-assist.md

Lines changed: 141 additions & 68 deletions
Large diffs are not rendered by default.

RELEASE_NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
### 4.7.0
22

33
* Added `AsyncSeq.splitAt` — splits a sequence at the given index, returning the first `count` elements as an array and the remaining elements as a new `AsyncSeq`. Mirrors `Seq.splitAt`. The source is enumerated once.
4+
* Added `AsyncSeq.removeAt` — returns a new sequence with the element at the specified index removed, mirroring `Seq.removeAt`.
5+
* Added `AsyncSeq.updateAt` — returns a new sequence with the element at the specified index replaced by a given value, mirroring `Seq.updateAt`.
6+
* Added `AsyncSeq.insertAt` — returns a new sequence with a value inserted before the element at the specified index (or appended if the index equals the sequence length), mirroring `Seq.insertAt`.
47

58
### 4.6.0
69

src/FSharp.Control.AsyncSeq/AsyncSeq.fs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,32 @@ module AsyncSeq =
14321432
let s = System.Collections.Generic.HashSet(excluded)
14331433
source |> filter (fun x -> not (s.Contains(x)))
14341434

1435+
let removeAt (index : int) (source : AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq {
1436+
if index < 0 then invalidArg "index" "must be non-negative"
1437+
let i = ref 0
1438+
for x in source do
1439+
if i.Value <> index then yield x
1440+
i := i.Value + 1 }
1441+
1442+
let updateAt (index : int) (value : 'T) (source : AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq {
1443+
if index < 0 then invalidArg "index" "must be non-negative"
1444+
let i = ref 0
1445+
for x in source do
1446+
if i.Value = index then yield value
1447+
else yield x
1448+
i := i.Value + 1 }
1449+
1450+
let insertAt (index : int) (value : 'T) (source : AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq {
1451+
if index < 0 then invalidArg "index" "must be non-negative"
1452+
let i = ref 0
1453+
for x in source do
1454+
if i.Value = index then yield value
1455+
yield x
1456+
i := i.Value + 1
1457+
if i.Value = index then yield value
1458+
elif i.Value < index then
1459+
invalidArg "index" "The index is outside the range of elements in the collection." }
1460+
14351461
#if !FABLE_COMPILER
14361462
let iterAsyncParallel (f:'a -> Async<unit>) (s:AsyncSeq<'a>) : Async<unit> = async {
14371463
use mb = MailboxProcessor.Start (ignore >> async.Return)

src/FSharp.Control.AsyncSeq/AsyncSeq.fsi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,19 @@ module AsyncSeq =
408408
/// in the given excluded collection. Uses a HashSet for O(1) lookup. Mirrors Seq.except.
409409
val except : excluded:seq<'T> -> source:AsyncSeq<'T> -> AsyncSeq<'T> when 'T : equality
410410

411+
/// Returns a new asynchronous sequence with the element at the specified index removed.
412+
/// Raises ArgumentException if index is negative. Mirrors Seq.removeAt.
413+
val removeAt : index:int -> source:AsyncSeq<'T> -> AsyncSeq<'T>
414+
415+
/// Returns a new asynchronous sequence with the element at the specified index replaced by the given value.
416+
/// Raises ArgumentException if index is negative. Mirrors Seq.updateAt.
417+
val updateAt : index:int -> value:'T -> source:AsyncSeq<'T> -> AsyncSeq<'T>
418+
419+
/// Returns a new asynchronous sequence with the given value inserted before the element at the specified index.
420+
/// An index equal to the length of the sequence appends the value at the end.
421+
/// Raises ArgumentException if index is negative or greater than the sequence length. Mirrors Seq.insertAt.
422+
val insertAt : index:int -> value:'T -> source:AsyncSeq<'T> -> AsyncSeq<'T>
423+
411424
/// Creates an asynchronous sequence that lazily takes element from an
412425
/// input synchronous sequence and returns them one-by-one.
413426
val ofSeq : source:seq<'T> -> AsyncSeq<'T>

tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3452,3 +3452,135 @@ let ``AsyncSeq.splitAt with count equal to length returns all in first and empty
34523452
let ``AsyncSeq.splitAt with negative count throws ArgumentException`` () =
34533453
Assert.Throws<System.ArgumentException>(fun () ->
34543454
AsyncSeq.splitAt -1 AsyncSeq.empty<int> |> Async.RunSynchronously |> ignore) |> ignore
3455+
3456+
// ===== removeAt =====
3457+
3458+
[<Test>]
3459+
let ``AsyncSeq.removeAt removes the element at the specified index`` () =
3460+
let result =
3461+
AsyncSeq.ofSeq [ 1; 2; 3; 4; 5 ]
3462+
|> AsyncSeq.removeAt 2
3463+
|> AsyncSeq.toArrayAsync
3464+
|> Async.RunSynchronously
3465+
Assert.AreEqual([| 1; 2; 4; 5 |], result)
3466+
3467+
[<Test>]
3468+
let ``AsyncSeq.removeAt removes the first element (index 0)`` () =
3469+
let result =
3470+
AsyncSeq.ofSeq [ 10; 20; 30 ]
3471+
|> AsyncSeq.removeAt 0
3472+
|> AsyncSeq.toArrayAsync
3473+
|> Async.RunSynchronously
3474+
Assert.AreEqual([| 20; 30 |], result)
3475+
3476+
[<Test>]
3477+
let ``AsyncSeq.removeAt removes the last element`` () =
3478+
let result =
3479+
AsyncSeq.ofSeq [ 1; 2; 3 ]
3480+
|> AsyncSeq.removeAt 2
3481+
|> AsyncSeq.toArrayAsync
3482+
|> Async.RunSynchronously
3483+
Assert.AreEqual([| 1; 2 |], result)
3484+
3485+
[<Test>]
3486+
let ``AsyncSeq.removeAt raises ArgumentException for negative index`` () =
3487+
Assert.Throws<System.ArgumentException>(fun () ->
3488+
AsyncSeq.ofSeq [ 1; 2; 3 ]
3489+
|> AsyncSeq.removeAt -1
3490+
|> AsyncSeq.toArrayAsync
3491+
|> Async.RunSynchronously |> ignore)
3492+
|> ignore
3493+
3494+
// ===== updateAt =====
3495+
3496+
[<Test>]
3497+
let ``AsyncSeq.updateAt replaces element at specified index`` () =
3498+
let result =
3499+
AsyncSeq.ofSeq [ 1; 2; 3; 4 ]
3500+
|> AsyncSeq.updateAt 1 99
3501+
|> AsyncSeq.toArrayAsync
3502+
|> Async.RunSynchronously
3503+
Assert.AreEqual([| 1; 99; 3; 4 |], result)
3504+
3505+
[<Test>]
3506+
let ``AsyncSeq.updateAt replaces first element`` () =
3507+
let result =
3508+
AsyncSeq.ofSeq [ 1; 2; 3 ]
3509+
|> AsyncSeq.updateAt 0 99
3510+
|> AsyncSeq.toArrayAsync
3511+
|> Async.RunSynchronously
3512+
Assert.AreEqual([| 99; 2; 3 |], result)
3513+
3514+
[<Test>]
3515+
let ``AsyncSeq.updateAt replaces last element`` () =
3516+
let result =
3517+
AsyncSeq.ofSeq [ 1; 2; 3 ]
3518+
|> AsyncSeq.updateAt 2 99
3519+
|> AsyncSeq.toArrayAsync
3520+
|> Async.RunSynchronously
3521+
Assert.AreEqual([| 1; 2; 99 |], result)
3522+
3523+
[<Test>]
3524+
let ``AsyncSeq.updateAt raises ArgumentException for negative index`` () =
3525+
Assert.Throws<System.ArgumentException>(fun () ->
3526+
AsyncSeq.ofSeq [ 1; 2; 3 ]
3527+
|> AsyncSeq.updateAt -1 0
3528+
|> AsyncSeq.toArrayAsync
3529+
|> Async.RunSynchronously |> ignore)
3530+
|> ignore
3531+
3532+
// ===== insertAt =====
3533+
3534+
[<Test>]
3535+
let ``AsyncSeq.insertAt inserts element at specified index`` () =
3536+
let result =
3537+
AsyncSeq.ofSeq [ 1; 2; 4; 5 ]
3538+
|> AsyncSeq.insertAt 2 3
3539+
|> AsyncSeq.toArrayAsync
3540+
|> Async.RunSynchronously
3541+
Assert.AreEqual([| 1; 2; 3; 4; 5 |], result)
3542+
3543+
[<Test>]
3544+
let ``AsyncSeq.insertAt inserts at index 0 (prepend)`` () =
3545+
let result =
3546+
AsyncSeq.ofSeq [ 2; 3 ]
3547+
|> AsyncSeq.insertAt 0 1
3548+
|> AsyncSeq.toArrayAsync
3549+
|> Async.RunSynchronously
3550+
Assert.AreEqual([| 1; 2; 3 |], result)
3551+
3552+
[<Test>]
3553+
let ``AsyncSeq.insertAt appends when index equals sequence length`` () =
3554+
let result =
3555+
AsyncSeq.ofSeq [ 1; 2; 3 ]
3556+
|> AsyncSeq.insertAt 3 4
3557+
|> AsyncSeq.toArrayAsync
3558+
|> Async.RunSynchronously
3559+
Assert.AreEqual([| 1; 2; 3; 4 |], result)
3560+
3561+
[<Test>]
3562+
let ``AsyncSeq.insertAt inserts into empty sequence at index 0`` () =
3563+
let result =
3564+
AsyncSeq.empty<int>
3565+
|> AsyncSeq.insertAt 0 42
3566+
|> AsyncSeq.toArrayAsync
3567+
|> Async.RunSynchronously
3568+
Assert.AreEqual([| 42 |], result)
3569+
3570+
[<Test>]
3571+
let ``AsyncSeq.insertAt raises ArgumentException for negative index`` () =
3572+
Assert.Throws<System.ArgumentException>(fun () ->
3573+
AsyncSeq.ofSeq [ 1; 2; 3 ]
3574+
|> AsyncSeq.insertAt -1 0
3575+
|> AsyncSeq.toArrayAsync
3576+
|> Async.RunSynchronously |> ignore)
3577+
|> ignore
3578+
3579+
[<Test>]
3580+
let ``AsyncSeq.insertAt raises ArgumentException when index exceeds length`` () =
3581+
Assert.Throws<System.ArgumentException>(fun () ->
3582+
AsyncSeq.ofSeq [ 1; 2; 3 ]
3583+
|> AsyncSeq.insertAt 5 0
3584+
|> AsyncSeq.toArrayAsync
3585+
|> Async.RunSynchronously |> ignore)
3586+
|> ignore

version.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>4.6.0</Version>
3+
<Version>4.7.0</Version>
44
</PropertyGroup>
55
</Project>

0 commit comments

Comments
 (0)