Skip to content

Commit 869a24d

Browse files
committed
2 parents 12b03a3 + 014649e commit 869a24d

10 files changed

Lines changed: 412 additions & 9 deletions

File tree

.config/dotnet-tools.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"version": 1,
33
"isRoot": true,
44
"tools": {
5-
"fsharp.formatting.commandtool": {
6-
"version": "11.4.3",
5+
"fsdocs-tool": {
6+
"version": "21.0.0",
77
"commands": [
88
"fsdocs"
99
],

RELEASE_NOTES.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
### 4.8.0
2+
3+
* Added `AsyncSeq.mapFoldAsync` — maps each element using an asynchronous folder that also threads an accumulator state, returning both the array of results and the final state; mirrors `Seq.mapFold`.
4+
* Added `AsyncSeq.mapFold` — synchronous variant of `AsyncSeq.mapFoldAsync`, mirroring `Seq.mapFold`.
5+
* Added `AsyncSeq.allPairs` — returns an async sequence of all pairs from two input sequences (cartesian product); the second source is fully buffered before iteration, mirroring `Seq.allPairs`.
6+
* Added `AsyncSeq.rev` — returns a new async sequence with all elements in reverse order; the entire source sequence is buffered before yielding, mirroring `Seq.rev`.
7+
8+
### 4.7.0
9+
10+
* 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.
11+
* Added `AsyncSeq.removeAt` — returns a new sequence with the element at the specified index removed, mirroring `Seq.removeAt`.
12+
* Added `AsyncSeq.updateAt` — returns a new sequence with the element at the specified index replaced by a given value, mirroring `Seq.updateAt`.
13+
* 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`.
14+
115
### 4.6.0
216

317
* Added `AsyncSeq.isEmpty` — returns `true` if the sequence contains no elements; short-circuits after the first element, mirroring `Seq.isEmpty`.

docs/AsyncSeq.fsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ keywords: F#, asynchronous sequences, AsyncSeq, IAsyncEnumerable, async workflow
1414
#r "FSharp.Control.AsyncSeq.dll"
1515
(*** condition: fsx ***)
1616
#if FSX
17-
#r "nuget: FSharp.Control.AsyncSeq,{{package-version}}"
17+
#r "nuget: FSharp.Control.AsyncSeq,{{fsdocs-package-version}}"
1818
#endif // FSX
1919
(*** condition: ipynb ***)
2020
#if IPYNB
21-
#r "nuget: FSharp.Control.AsyncSeq,{{package-version}}"
21+
#r "nuget: FSharp.Control.AsyncSeq,{{fsdocs-package-version}}"
2222
#endif // IPYNB
2323

2424

docs/AsyncSeqExamples.fsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ keywords: F#, asynchronous sequences, AsyncSeq, examples
1414
#r "FSharp.Control.AsyncSeq.dll"
1515
(*** condition: fsx ***)
1616
#if FSX
17-
#r "nuget: FSharp.Control.AsyncSeq,{{package-version}}"
17+
#r "nuget: FSharp.Control.AsyncSeq,{{fsdocs-package-version}}"
1818
#endif // FSX
1919
(*** condition: ipynb ***)
2020
#if IPYNB
21-
#r "nuget: FSharp.Control.AsyncSeq,{{package-version}}"
21+
#r "nuget: FSharp.Control.AsyncSeq,{{fsdocs-package-version}}"
2222
#endif // IPYNB
2323

2424
(**

docs/ComparisonWithObservable.fsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ keywords: F#, asynchronous sequences, AsyncSeq, IObservable, reactive
1414
#r "FSharp.Control.AsyncSeq.dll"
1515
(*** condition: fsx ***)
1616
#if FSX
17-
#r "nuget: FSharp.Control.AsyncSeq,{{package-version}}"
17+
#r "nuget: FSharp.Control.AsyncSeq,{{fsdocs-package-version}}"
1818
#endif // FSX
1919
(*** condition: ipynb ***)
2020
#if IPYNB
21-
#r "nuget: FSharp.Control.AsyncSeq,{{package-version}}"
21+
#r "nuget: FSharp.Control.AsyncSeq,{{fsdocs-package-version}}"
2222
#endif // IPYNB
2323

2424

src/FSharp.Control.AsyncSeq/AsyncSeq.fs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,23 @@ module AsyncSeq =
12951295
let reduce (f: 'T -> 'T -> 'T) (source: AsyncSeq<'T>) : Async<'T> =
12961296
reduceAsync (fun a b -> f a b |> async.Return) source
12971297

1298+
let mapFoldAsync (folder: 'State -> 'T -> Async<'Result * 'State>) (state: 'State) (source: AsyncSeq<'T>) : Async<'Result array * 'State> = async {
1299+
let results = ResizeArray<'Result>()
1300+
let mutable st = state
1301+
use ie = source.GetEnumerator()
1302+
let! move = ie.MoveNext()
1303+
let b = ref move
1304+
while b.Value.IsSome do
1305+
let! (r, st') = folder st b.Value.Value
1306+
results.Add(r)
1307+
st <- st'
1308+
let! next = ie.MoveNext()
1309+
b := next
1310+
return (results.ToArray(), st) }
1311+
1312+
let mapFold (folder: 'State -> 'T -> 'Result * 'State) (state: 'State) (source: AsyncSeq<'T>) : Async<'Result array * 'State> =
1313+
mapFoldAsync (fun st x -> folder st x |> async.Return) state source
1314+
12981315
let length (source : AsyncSeq<'T>) =
12991316
fold (fun st _ -> st + 1L) 0L source
13001317

@@ -1432,6 +1449,32 @@ module AsyncSeq =
14321449
let s = System.Collections.Generic.HashSet(excluded)
14331450
source |> filter (fun x -> not (s.Contains(x)))
14341451

1452+
let removeAt (index : int) (source : AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq {
1453+
if index < 0 then invalidArg "index" "must be non-negative"
1454+
let i = ref 0
1455+
for x in source do
1456+
if i.Value <> index then yield x
1457+
i := i.Value + 1 }
1458+
1459+
let updateAt (index : int) (value : 'T) (source : AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq {
1460+
if index < 0 then invalidArg "index" "must be non-negative"
1461+
let i = ref 0
1462+
for x in source do
1463+
if i.Value = index then yield value
1464+
else yield x
1465+
i := i.Value + 1 }
1466+
1467+
let insertAt (index : int) (value : 'T) (source : AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq {
1468+
if index < 0 then invalidArg "index" "must be non-negative"
1469+
let i = ref 0
1470+
for x in source do
1471+
if i.Value = index then yield value
1472+
yield x
1473+
i := i.Value + 1
1474+
if i.Value = index then yield value
1475+
elif i.Value < index then
1476+
invalidArg "index" "The index is outside the range of elements in the collection." }
1477+
14351478
#if !FABLE_COMPILER
14361479
let iterAsyncParallel (f:'a -> Async<unit>) (s:AsyncSeq<'a>) : Async<unit> = async {
14371480
use mb = MailboxProcessor.Start (ignore >> async.Return)
@@ -1676,6 +1719,25 @@ module AsyncSeq =
16761719
let zipWith3 (f:'T1 -> 'T2 -> 'T3 -> 'U) (source1:AsyncSeq<'T1>) (source2:AsyncSeq<'T2>) (source3:AsyncSeq<'T3>) : AsyncSeq<'U> =
16771720
zipWithAsync3 (fun a b c -> f a b c |> async.Return) source1 source2 source3
16781721

1722+
let allPairs (source1: AsyncSeq<'T1>) (source2: AsyncSeq<'T2>) : AsyncSeq<'T1 * 'T2> = asyncSeq {
1723+
let buf = System.Collections.Generic.List<'T2>()
1724+
use ie2 = source2.GetEnumerator()
1725+
let! move2 = ie2.MoveNext()
1726+
let b2 = ref move2
1727+
while b2.Value.IsSome do
1728+
buf.Add(b2.Value.Value)
1729+
let! next2 = ie2.MoveNext()
1730+
b2 := next2
1731+
use ie1 = source1.GetEnumerator()
1732+
let! move1 = ie1.MoveNext()
1733+
let b1 = ref move1
1734+
while b1.Value.IsSome do
1735+
let x = b1.Value.Value
1736+
for y in buf do
1737+
yield (x, y)
1738+
let! next1 = ie1.MoveNext()
1739+
b1 := next1 }
1740+
16791741
let zappAsync (fs:AsyncSeq<'T -> Async<'U>>) (s:AsyncSeq<'T>) : AsyncSeq<'U> =
16801742
zipWithAsync (|>) s fs
16811743

@@ -1815,6 +1877,35 @@ module AsyncSeq =
18151877

18161878
let tail (source : AsyncSeq<'T>) : AsyncSeq<'T> = skip 1 source
18171879

1880+
/// Splits an async sequence at the given index, returning the first `count` elements as an array
1881+
/// and the remaining elements as a new AsyncSeq. The source is enumerated once.
1882+
let splitAt (count: int) (source: AsyncSeq<'T>) : Async<'T array * AsyncSeq<'T>> = async {
1883+
if count < 0 then invalidArg "count" "must be non-negative"
1884+
let ie = source.GetEnumerator()
1885+
let ra = ResizeArray<'T>()
1886+
let! m = ie.MoveNext()
1887+
let b = ref m
1888+
while b.Value.IsSome && ra.Count < count do
1889+
ra.Add b.Value.Value
1890+
let! next = ie.MoveNext()
1891+
b := next
1892+
let first = ra.ToArray()
1893+
let rest =
1894+
if b.Value.IsNone then
1895+
ie.Dispose()
1896+
empty<'T>
1897+
else
1898+
let cur = ref b.Value
1899+
asyncSeq {
1900+
try
1901+
while cur.Value.IsSome do
1902+
yield cur.Value.Value
1903+
let! next = ie.MoveNext()
1904+
cur := next
1905+
finally
1906+
ie.Dispose() }
1907+
return first, rest }
1908+
18181909
let toArrayAsync (source : AsyncSeq<'T>) : Async<'T[]> = async {
18191910
let ra = (new ResizeArray<_>())
18201911
use ie = source.GetEnumerator()
@@ -1939,6 +2030,11 @@ module AsyncSeq =
19392030

19402031
let sortWith (comparer:'T -> 'T -> int) (source:AsyncSeq<'T>) : array<'T> =
19412032
toSortedSeq (Array.sortWith comparer) source
2033+
2034+
let rev (source: AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq {
2035+
let! arr = toArrayAsync source
2036+
for i in arr.Length - 1 .. -1 .. 0 do
2037+
yield arr.[i] }
19422038
#endif
19432039

19442040
#if !FABLE_COMPILER

src/FSharp.Control.AsyncSeq/AsyncSeq.fsi

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,16 @@ module AsyncSeq =
258258
/// specified 'reduction' function. Raises InvalidOperationException if the sequence is empty.
259259
val reduce : reduction:('T -> 'T -> 'T) -> source:AsyncSeq<'T> -> Async<'T>
260260

261+
/// Asynchronously maps each element of the async sequence with an asynchronous folder function that
262+
/// also threads an accumulator state through the computation. Returns the array of results and the
263+
/// final state, mirroring Seq.mapFold.
264+
val mapFoldAsync : folder:('State -> 'T -> Async<'Result * 'State>) -> state:'State -> source:AsyncSeq<'T> -> Async<'Result array * 'State>
265+
266+
/// Maps each element of the async sequence with a folder function that also threads an accumulator
267+
/// state through the computation. Returns the array of results and the final state,
268+
/// mirroring Seq.mapFold.
269+
val mapFold : folder:('State -> 'T -> 'Result * 'State) -> state:'State -> source:AsyncSeq<'T> -> Async<'Result array * 'State>
270+
261271
/// Asynchronously sum the elements of the input asynchronous sequence using the specified function.
262272
val inline sum : source:AsyncSeq< ^T > -> Async< ^T>
263273
when ^T : (static member ( + ) : ^T * ^T -> ^T)
@@ -408,6 +418,19 @@ module AsyncSeq =
408418
/// in the given excluded collection. Uses a HashSet for O(1) lookup. Mirrors Seq.except.
409419
val except : excluded:seq<'T> -> source:AsyncSeq<'T> -> AsyncSeq<'T> when 'T : equality
410420

421+
/// Returns a new asynchronous sequence with the element at the specified index removed.
422+
/// Raises ArgumentException if index is negative. Mirrors Seq.removeAt.
423+
val removeAt : index:int -> source:AsyncSeq<'T> -> AsyncSeq<'T>
424+
425+
/// Returns a new asynchronous sequence with the element at the specified index replaced by the given value.
426+
/// Raises ArgumentException if index is negative. Mirrors Seq.updateAt.
427+
val updateAt : index:int -> value:'T -> source:AsyncSeq<'T> -> AsyncSeq<'T>
428+
429+
/// Returns a new asynchronous sequence with the given value inserted before the element at the specified index.
430+
/// An index equal to the length of the sequence appends the value at the end.
431+
/// Raises ArgumentException if index is negative or greater than the sequence length. Mirrors Seq.insertAt.
432+
val insertAt : index:int -> value:'T -> source:AsyncSeq<'T> -> AsyncSeq<'T>
433+
411434
/// Creates an asynchronous sequence that lazily takes element from an
412435
/// input synchronous sequence and returns them one-by-one.
413436
val ofSeq : source:seq<'T> -> AsyncSeq<'T>
@@ -483,6 +506,10 @@ module AsyncSeq =
483506
/// The resulting sequence stops when any of the argument sequences stop.
484507
val zipWith3 : mapping:('T1 -> 'T2 -> 'T3 -> 'U) -> source1:AsyncSeq<'T1> -> source2:AsyncSeq<'T2> -> source3:AsyncSeq<'T3> -> AsyncSeq<'U>
485508

509+
/// Returns an async sequence of all pairs of elements from the two input sequences.
510+
/// The second sequence is fully buffered before iteration begins, mirroring Seq.allPairs.
511+
val allPairs : source1:AsyncSeq<'T1> -> source2:AsyncSeq<'T2> -> AsyncSeq<'T1 * 'T2>
512+
486513
/// Builds a new asynchronous sequence whose elements are generated by
487514
/// applying the specified function to all elements of the input sequence.
488515
///
@@ -586,6 +613,11 @@ module AsyncSeq =
586613
/// Returns an empty sequence if the source is empty.
587614
val tail : source:AsyncSeq<'T> -> AsyncSeq<'T>
588615

616+
/// Splits an async sequence at the given index. Returns an async computation that yields
617+
/// the first `count` elements as an array and the remaining elements as a new AsyncSeq.
618+
/// The source is enumerated once; the returned AsyncSeq lazily produces the remainder.
619+
val splitAt : count:int -> source:AsyncSeq<'T> -> Async<'T array * AsyncSeq<'T>>
620+
589621
/// Creates an async computation which iterates the AsyncSeq and collects the output into an array.
590622
val toArrayAsync : source:AsyncSeq<'T> -> Async<'T []>
591623

@@ -636,6 +668,11 @@ module AsyncSeq =
636668
/// that sequence is iterated. As a result this function should not be used with
637669
/// large or infinite sequences.
638670
val sortWith : comparer:('T -> 'T -> int) -> source:AsyncSeq<'T> -> array<'T>
671+
672+
/// Returns a new async sequence with the elements in reverse order. The entire source
673+
/// sequence is buffered before yielding any elements, mirroring Seq.rev.
674+
/// This function should not be used with large or infinite sequences.
675+
val rev : source:AsyncSeq<'T> -> AsyncSeq<'T>
639676
#endif
640677

641678
/// Interleaves two async sequences of the same type into a resulting sequence. The provided

src/FSharp.Control.AsyncSeq/FSharp.Control.AsyncSeq.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
<PackageProjectUrl>https://fsprojects.github.io/FSharp.Control.AsyncSeq/</PackageProjectUrl>
1010
<PackageIconUrl>https://fsprojects.github.io/FSharp.Control.AsyncSeq/img/logo.png</PackageIconUrl>
1111
<PackageTags>F#;async;fsharp;streaming</PackageTags>
12+
<FsDocsLicenseLink>https://github.com/fsprojects/FSharp.Control.AsyncSeq/blob/main/LICENSE.md</FsDocsLicenseLink>
13+
<FsDocsReleaseNotesLink>https://github.com/fsprojects/FSharp.Control.AsyncSeq/blob/main/RELEASE_NOTES.md</FsDocsReleaseNotesLink>
1214
<IncludeSymbols>true</IncludeSymbols>
1315
<RepositoryUrl>https://github.com/fsprojects/FSharp.Control.AsyncSeq</RepositoryUrl>
1416
<SymbolPackageFormat>snupkg</SymbolPackageFormat>

0 commit comments

Comments
 (0)