diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
index 4b780bf4..18c61e7b 100644
--- a/.github/workflows/publish.yaml
+++ b/.github/workflows/publish.yaml
@@ -26,7 +26,10 @@ jobs:
- name: Nuget publish
# skip-duplicate ensures that the 409 error received when the package was already published,
# will just issue a warning and won't have the GH action fail.
- # NUGET_PUBLISH_TOKEN_TASKSEQ is valid until approx. 8 Nov 2023 and will need to be updated by then.
- # do so under https://github.com/fsprojects/FSharp.Control.TaskSeq/settings/secrets/actions
- # select button "Add repository secret" or update the existing one under "Repository secrets"
+ # NUGET_PUBLISH_TOKEN_TASKSEQ is valid until approx. 11 Dec 2024 and will need to be updated by then:
+ # - log in to Nuget.org using 'abelbraaksma' admin account and then refresh the token in Nuget
+ # - copy the token
+ # - go to https://github.com/fsprojects/FSharp.Control.TaskSeq/settings/secrets/actions
+ # - select button "Add repository secret" or update the existing one under "Repository secrets"
+ # - rerun the job
run: dotnet nuget push packages\FSharp.Control.TaskSeq.*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_PUBLISH_TOKEN_TASKSEQ }} --skip-duplicate
diff --git a/README.md b/README.md
index dbfdcd12..cdf279f7 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@ As with `seq` and `Seq`, this library comes with a bunch of well-known collectio
### `taskSeq` computation expressions
The `taskSeq` computation expression can be used just like using `seq`.
-Additionally, it adds support for working with `Task`s through `let!` and
+Additionally, it adds support for working with `Task`s through `let!` and
looping over both normal and asynchronous sequences (ones that implement
`IAsyncEnumerable<'T>'`). You can use `yield!` and `yield` and there's support
for `use` and `use!`, `try-with` and `try-finally` and `while` loops within
@@ -127,7 +127,7 @@ let helloTs = taskSeq { yield "Hello, World!" }
let f() = task {
// using toList forces execution of whole sequence
let! hello = TaskSeq.toList helloTs // toList returns a Task<'T list>
- return List.head hello
+ return List.head hello
}
// can be mixed with normal sequences
@@ -147,15 +147,15 @@ let allFilesAsLines() = taskSeq {
yield! contents
}
-let write file =
+let write file =
allFilesAsLines()
-
+
// synchronous map function on asynchronous task sequence
|> TaskSeq.map (fun x -> x.Replace("a", "b"))
// asynchronous map
|> TaskSeq.mapAsync (fun x -> task { return "hello: " + x })
-
+
// asynchronous iter
|> TaskSeq.iterAsync (fun data -> File.WriteAllTextAsync(fileName, data))
@@ -227,142 +227,141 @@ We are working hard on getting a full set of module functions on `TaskSeq` that
The following is the progress report:
-| Done | `Seq` | `TaskSeq` | Variants | Remarks |
-|------------------|--------------------|-----------------|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| ❓ | `allPairs` | `allPairs` | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ✅ [#81][] | `append` | `append` | | |
-| ✅ [#81][] | | | `appendSeq` | |
-| ✅ [#81][] | | | `prependSeq` | |
-| | `average` | `average` | | |
-| | `averageBy` | `averageBy` | `averageByAsync` | |
-| ❓ | `cache` | `cache` | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ✅ [#67][] | `cast` | `cast` | | |
-| ✅ [#67][] | | | `box` | |
-| ✅ [#67][] | | | `unbox` | |
-| ✅ [#23][] | `choose` | `choose` | `chooseAsync` | |
-| | `chunkBySize` | `chunkBySize` | | |
-| ✅ [#11][] | `collect` | `collect` | `collectAsync` | |
-| ✅ [#11][] | | `collectSeq` | `collectSeqAsync` | |
-| | `compareWith` | `compareWith` | `compareWithAsync` | |
-| ✅ [#69][] | `concat` | `concat` | | |
-| ✅ [#70][] | `contains` | `contains` | | |
-| ✅ [#82][] | `delay` | `delay` | | |
-| | `distinct` | `distinct` | | |
-| | `distinctBy` | `dictinctBy` | `distinctByAsync` | |
-| ✅ [#2][] | `empty` | `empty` | | |
-| ✅ [#23][] | `exactlyOne` | `exactlyOne` | | |
-| ✅ [#83][] | `except` | `except` | | |
-| ✅ [#83][] | | `exceptOfSeq` | | |
-| ✅ [#70][] | `exists` | `exists` | `existsAsync` | |
-| | `exists2` | `exists2` | | |
-| ✅ [#23][] | `filter` | `filter` | `filterAsync` | |
-| ✅ [#23][] | `find` | `find` | `findAsync` | |
-| 🚫 | `findBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#68][] | `findIndex` | `findIndex` | `findIndexAsync` | |
-| 🚫 | `findIndexBack` | n/a | n/a | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#2][] | `fold` | `fold` | `foldAsync` | |
-| | `fold2` | `fold2` | `fold2Async` | |
-| 🚫 | `foldBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| 🚫 | `foldBack2` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| | `forall` | `forall` | `forallAsync` | |
-| | `forall2` | `forall2` | `forall2Async` | |
-| ❓ | `groupBy` | `groupBy` | `groupByAsync` | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ✅ [#23][] | `head` | `head` | | |
-| ✅ [#68][] | `indexed` | `indexed` | | |
-| ✅ [#69][] | `init` | `init` | `initAsync` | |
-| ✅ [#69][] | `initInfinite` | `initInfinite` | `initInfiniteAsync` | |
-| | `insertAt` | `insertAt` | | |
-| | `insertManyAt` | `insertManyAt` | | |
-| ✅ [#23][] | `isEmpty` | `isEmpty` | | |
-| ✅ [#23][] | `item` | `item` | | |
-| ✅ [#2][] | `iter` | `iter` | `iterAsync` | |
-| | `iter2` | `iter2` | `iter2Async` | |
-| ✅ [#2][] | `iteri` | `iteri` | `iteriAsync` | |
-| | `iteri2` | `iteri2` | `iteri2Async` | |
-| ✅ [#23][] | `last` | `last` | | |
-| ✅ [#53][] | `length` | `length` | | |
-| ✅ [#53][] | | `lengthBy` | `lengthByAsync` | |
-| ✅ [#2][] | `map` | `map` | `mapAsync` | |
-| | `map2` | `map2` | `map2Async` | |
-| | `map3` | `map3` | `map3Async` | |
-| | `mapFold` | `mapFold` | `mapFoldAsync` | |
-| 🚫 | `mapFoldBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#2][] | `mapi` | `mapi` | `mapiAsync` | |
-| | `mapi2` | `mapi2` | `mapi2Async` | |
-| | `max` | `max` | | |
-| | `maxBy` | `maxBy` | `maxByAsync` | |
-| | `min` | `min` | | |
-| | `minBy` | `minBy` | `minByAsync` | |
-| ✅ [#2][] | `ofArray` | `ofArray` | | |
-| ✅ [#2][] | | `ofAsyncArray` | | |
-| ✅ [#2][] | | `ofAsyncList` | | |
-| ✅ [#2][] | | `ofAsyncSeq` | | |
-| ✅ [#2][] | `ofList` | `ofList` | | |
-| ✅ [#2][] | | `ofTaskList` | | |
-| ✅ [#2][] | | `ofResizeArray` | | |
-| ✅ [#2][] | | `ofSeq` | | |
-| ✅ [#2][] | | `ofTaskArray` | | |
-| ✅ [#2][] | | `ofTaskList` | | |
-| ✅ [#2][] | | `ofTaskSeq` | | |
-| | `pairwise` | `pairwise` | | |
-| | `permute` | `permute` | `permuteAsync` | |
-| ✅ [#23][] | `pick` | `pick` | `pickAsync` | |
-| 🚫 | `readOnly` | | | [note #3](#note3 "The motivation for 'readOnly' in 'Seq' is that a cast from a mutable array or list to a 'seq<_>' is valid and can be cast back, leading to a mutable sequence. Since 'TaskSeq' doesn't implement 'IEnumerable<_>', such casts are not possible.") |
-| | `reduce` | `reduce` | `reduceAsync` | |
-| 🚫 | `reduceBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| | `removeAt` | `removeAt` | | |
-| | `removeManyAt` | `removeManyAt` | | |
-| | `replicate` | `replicate` | | |
-| ❓ | `rev` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| | `scan` | `scan` | `scanAsync` | |
-| 🚫 | `scanBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#90][] | `singleton` | `singleton` | | |
-| | `skip` | `skip` | | |
-| | `skipWhile` | `skipWhile` | `skipWhileAsync` | |
-| | | | `skipWhileInclusive` | |
-| | | | `skipWhileInclusiveAsync` | |
-| ❓ | `sort` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ❓ | `sortBy` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ❓ | `sortByAscending` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ❓ | `sortByDescending` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ❓ | `sortWith` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| | `splitInto` | `splitInto` | | |
-| | `sum` | `sum` | | |
-| | `sumBy` | `sumBy` | `sumByAsync` | |
-| ✅ [#76][] | `tail` | `tail` | | |
-| | `take` | `take` | | |
-| ✅ [#126][]| `takeWhile` | `takeWhile` | `takeWhileAsync` | |
-| ✅ [#126][]| | | `takeWhileInclusive` | |
-| ✅ [#126][]| | | `takeWhileInclusiveAsync`| |
-| ✅ [#2][] | `toArray` | `toArray` | `toArrayAsync` | |
-| ✅ [#2][] | | `toIList` | `toIListAsync` | |
-| ✅ [#2][] | `toList` | `toList` | `toListAsync` | |
-| ✅ [#2][] | | `toResizeArray` | `toResizeArrayAsync` | |
-| ✅ [#2][] | | `toSeq` | `toSeqAsync` | |
-| | | […] | | |
-| ❓ | `transpose` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| | `truncate` | `truncate` | | |
-| ✅ [#23][] | `tryExactlyOne` | `tryExactlyOne` | `tryExactlyOneAsync` | |
-| ✅ [#23][] | `tryFind` | `tryFind` | `tryFindAsync` | |
-| 🚫 | `tryFindBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#68][] | `tryFindIndex` | `tryFindIndex` | `tryFindIndexAsync` | |
-| 🚫 | `tryFindIndexBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#23][] | `tryHead` | `tryHead` | | |
-| ✅ [#23][] | `tryItem` | `tryItem` | | |
-| ✅ [#23][] | `tryLast` | `tryLast` | | |
-| ✅ [#23][] | `tryPick` | `tryPick` | `tryPickAsync` | |
-| ✅ [#76][] | | `tryTail` | | |
-| | `unfold` | `unfold` | `unfoldAsync` | |
-| | `updateAt` | `updateAt` | | |
-| | `where` | `where` | `whereAsync` | |
-| | `windowed` | `windowed` | | |
-| ✅ [#2][] | `zip` | `zip` | | |
-| | `zip3` | `zip3` | | |
-| | | `zip4` | | |
-
-
-¹⁾ _These functions require a form of pre-materializing through `TaskSeq.cache`, similar to the approach taken in the corresponding `Seq` functions. It doesn't make much sense to have a cached async sequence. However, `AsyncSeq` does implement these, so we'll probably do so eventually as well._
-²⁾ _Because of the async nature of `TaskSeq` sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the `xxxBack` iterators._
+| Done | `Seq` | `TaskSeq` | Variants | Remarks |
+|------------------|--------------------|----------------------|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| ❓ | `allPairs` | `allPairs` | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ✅ [#81][] | `append` | `append` | | |
+| ✅ [#81][] | | | `appendSeq` | |
+| ✅ [#81][] | | | `prependSeq` | |
+| | `average` | `average` | | |
+| | `averageBy` | `averageBy` | `averageByAsync` | |
+| ❓ | `cache` | `cache` | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ✅ [#67][] | `cast` | `cast` | | |
+| ✅ [#67][] | | | `box` | |
+| ✅ [#67][] | | | `unbox` | |
+| ✅ [#23][] | `choose` | `choose` | `chooseAsync` | |
+| | `chunkBySize` | `chunkBySize` | | |
+| ✅ [#11][] | `collect` | `collect` | `collectAsync` | |
+| ✅ [#11][] | | `collectSeq` | `collectSeqAsync` | |
+| | `compareWith` | `compareWith` | `compareWithAsync` | |
+| ✅ [#69][] | `concat` | `concat` | | |
+| ✅ [#70][] | `contains` | `contains` | | |
+| ✅ [#82][] | `delay` | `delay` | | |
+| | `distinct` | `distinct` | | |
+| | `distinctBy` | `dictinctBy` | `distinctByAsync` | |
+| ✅ [#2][] | `empty` | `empty` | | |
+| ✅ [#23][] | `exactlyOne` | `exactlyOne` | | |
+| ✅ [#83][] | `except` | `except` | | |
+| ✅ [#83][] | | `exceptOfSeq` | | |
+| ✅ [#70][] | `exists` | `exists` | `existsAsync` | |
+| | `exists2` | `exists2` | | |
+| ✅ [#23][] | `filter` | `filter` | `filterAsync` | |
+| ✅ [#23][] | `find` | `find` | `findAsync` | |
+| 🚫 | `findBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#68][] | `findIndex` | `findIndex` | `findIndexAsync` | |
+| 🚫 | `findIndexBack` | n/a | n/a | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#2][] | `fold` | `fold` | `foldAsync` | |
+| | `fold2` | `fold2` | `fold2Async` | |
+| 🚫 | `foldBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| 🚫 | `foldBack2` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| | `forall` | `forall` | `forallAsync` | |
+| | `forall2` | `forall2` | `forall2Async` | |
+| ❓ | `groupBy` | `groupBy` | `groupByAsync` | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ✅ [#23][] | `head` | `head` | | |
+| ✅ [#68][] | `indexed` | `indexed` | | |
+| ✅ [#69][] | `init` | `init` | `initAsync` | |
+| ✅ [#69][] | `initInfinite` | `initInfinite` | `initInfiniteAsync` | |
+| | `insertAt` | `insertAt` | | |
+| | `insertManyAt` | `insertManyAt` | | |
+| ✅ [#23][] | `isEmpty` | `isEmpty` | | |
+| ✅ [#23][] | `item` | `item` | | |
+| ✅ [#2][] | `iter` | `iter` | `iterAsync` | |
+| | `iter2` | `iter2` | `iter2Async` | |
+| ✅ [#2][] | `iteri` | `iteri` | `iteriAsync` | |
+| | `iteri2` | `iteri2` | `iteri2Async` | |
+| ✅ [#23][] | `last` | `last` | | |
+| ✅ [#53][] | `length` | `length` | | |
+| ✅ [#53][] | | `lengthBy` | `lengthByAsync` | |
+| ✅ [#2][] | `map` | `map` | `mapAsync` | |
+| | `map2` | `map2` | `map2Async` | |
+| | `map3` | `map3` | `map3Async` | |
+| | `mapFold` | `mapFold` | `mapFoldAsync` | |
+| 🚫 | `mapFoldBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#2][] | `mapi` | `mapi` | `mapiAsync` | |
+| | `mapi2` | `mapi2` | `mapi2Async` | |
+| | `max` | `max` | | |
+| | `maxBy` | `maxBy` | `maxByAsync` | |
+| | `min` | `min` | | |
+| | `minBy` | `minBy` | `minByAsync` | |
+| ✅ [#2][] | `ofArray` | `ofArray` | | |
+| ✅ [#2][] | | `ofAsyncArray` | | |
+| ✅ [#2][] | | `ofAsyncList` | | |
+| ✅ [#2][] | | `ofAsyncSeq` | | |
+| ✅ [#2][] | `ofList` | `ofList` | | |
+| ✅ [#2][] | | `ofTaskList` | | |
+| ✅ [#2][] | | `ofResizeArray` | | |
+| ✅ [#2][] | | `ofSeq` | | |
+| ✅ [#2][] | | `ofTaskArray` | | |
+| ✅ [#2][] | | `ofTaskList` | | |
+| ✅ [#2][] | | `ofTaskSeq` | | |
+| | `pairwise` | `pairwise` | | |
+| | `permute` | `permute` | `permuteAsync` | |
+| ✅ [#23][] | `pick` | `pick` | `pickAsync` | |
+| 🚫 | `readOnly` | | | [note #3](#note3 "The motivation for 'readOnly' in 'Seq' is that a cast from a mutable array or list to a 'seq<_>' is valid and can be cast back, leading to a mutable sequence. Since 'TaskSeq' doesn't implement 'IEnumerable<_>', such casts are not possible.") |
+| | `reduce` | `reduce` | `reduceAsync` | |
+| 🚫 | `reduceBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| | `removeAt` | `removeAt` | | |
+| | `removeManyAt` | `removeManyAt` | | |
+| | `replicate` | `replicate` | | |
+| ❓ | `rev` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| | `scan` | `scan` | `scanAsync` | |
+| 🚫 | `scanBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#90][] | `singleton` | `singleton` | | |
+| ✅ [#209][]| `skip` | `skip` | | |
+| ✅ [#209][]| | `drop` | | |
+| | `skipWhile` | `skipWhile` | `skipWhileAsync` | |
+| | | `skipWhileInclusive` | `skipWhileInclusiveAsync` | |
+| ❓ | `sort` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ❓ | `sortBy` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ❓ | `sortByAscending` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ❓ | `sortByDescending` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ❓ | `sortWith` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| | `splitInto` | `splitInto` | | |
+| | `sum` | `sum` | | |
+| | `sumBy` | `sumBy` | `sumByAsync` | |
+| ✅ [#76][] | `tail` | `tail` | | |
+| ✅ [#209][]| `take` | `take` | | |
+| ✅ [#126][]| `takeWhile` | `takeWhile` | `takeWhileAsync` | |
+| ✅ [#126][]| | `takeWhileInclusive` | `takeWhileInclusiveAsync` | |
+| ✅ [#2][] | `toArray` | `toArray` | `toArrayAsync` | |
+| ✅ [#2][] | | `toIList` | `toIListAsync` | |
+| ✅ [#2][] | `toList` | `toList` | `toListAsync` | |
+| ✅ [#2][] | | `toResizeArray` | `toResizeArrayAsync` | |
+| ✅ [#2][] | | `toSeq` | `toSeqAsync` | |
+| | | […] | | |
+| ❓ | `transpose` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ✅ [#209][]| `truncate` | `truncate` | | |
+| ✅ [#23][] | `tryExactlyOne` | `tryExactlyOne` | `tryExactlyOneAsync` | |
+| ✅ [#23][] | `tryFind` | `tryFind` | `tryFindAsync` | |
+| 🚫 | `tryFindBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#68][] | `tryFindIndex` | `tryFindIndex` | `tryFindIndexAsync` | |
+| 🚫 | `tryFindIndexBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#23][] | `tryHead` | `tryHead` | | |
+| ✅ [#23][] | `tryItem` | `tryItem` | | |
+| ✅ [#23][] | `tryLast` | `tryLast` | | |
+| ✅ [#23][] | `tryPick` | `tryPick` | `tryPickAsync` | |
+| ✅ [#76][] | | `tryTail` | | |
+| | `unfold` | `unfold` | `unfoldAsync` | |
+| | `updateAt` | `updateAt` | | |
+| | `where` | `where` | `whereAsync` | |
+| | `windowed` | `windowed` | | |
+| ✅ [#2][] | `zip` | `zip` | | |
+| | `zip3` | `zip3` | | |
+| | | `zip4` | | |
+
+
+¹⁾ _These functions require a form of pre-materializing through `TaskSeq.cache`, similar to the approach taken in the corresponding `Seq` functions. It doesn't make much sense to have a cached async sequence. However, `AsyncSeq` does implement these, so we'll probably do so eventually as well._
+²⁾ _Because of the async nature of `TaskSeq` sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the `xxxBack` iterators._
³⁾ _The motivation for `readOnly` in `Seq` is that a cast from a mutable array or list to a `seq<_>` is valid and can be cast back, leading to a mutable sequence. Since `TaskSeq` doesn't implement `IEnumerable<_>`, such casts are not possible._
## More information
@@ -476,6 +475,7 @@ module TaskSeq =
val concat: sources: TaskSeq<#TaskSeq<'T>> -> TaskSeq<'T>
val contains<'T when 'T: equality> : value: 'T -> source: TaskSeq<'T> -> Task
val delay: generator: (unit -> TaskSeq<'T>) -> TaskSeq<'T>
+ val drop: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
val empty<'T> : TaskSeq<'T>
val exactlyOne: source: TaskSeq<'T> -> Task<'T>
val except<'T when 'T: equality> : itemsToExclude: TaskSeq<'T> -> source: TaskSeq<'T> -> TaskSeq<'T>
@@ -525,7 +525,9 @@ module TaskSeq =
val pickAsync: chooser: ('T -> #Task<'U option>) -> source: TaskSeq<'T> -> Task<'U>
val prependSeq: source1: seq<'T> -> source2: TaskSeq<'T> -> TaskSeq<'T>
val singleton: source: 'T -> TaskSeq<'T>
+ val skip: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
val tail: source: TaskSeq<'T> -> Task>
+ val take: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
val takeWhile: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task>
val takeWhileAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task>
val takeWhileInclusive: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task>
@@ -537,6 +539,7 @@ module TaskSeq =
val toListAsync: source: TaskSeq<'T> -> Task<'T list>
val toResizeArrayAsync: source: TaskSeq<'T> -> Task>
val toSeq: source: TaskSeq<'T> -> seq<'T>
+ val truncate: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
val tryExactlyOne: source: TaskSeq<'T> -> Task<'T option>
val tryFind: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<'T option>
val tryFindAsync: predicate: ('T -> #Task) -> source: TaskSeq<'T> -> Task<'T option>
@@ -596,6 +599,7 @@ module TaskSeq =
[#90]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/90
[#126]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/126
[#133]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/133
+[#209]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/209
[issues]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues
[nuget]: https://www.nuget.org/packages/FSharp.Control.TaskSeq/
diff --git a/assets/nuget-package-readme.md b/assets/nuget-package-readme.md
index 35e6320c..440af182 100644
--- a/assets/nuget-package-readme.md
+++ b/assets/nuget-package-readme.md
@@ -1,6 +1,9 @@
# TaskSeq
-An implementation of [`IAsyncEnumerable<'T>`][3] as a computation expression: `taskSeq { ... }` with an accompanying `TaskSeq` module.
+An implementation of [`IAsyncEnumerable<'T>`][3] as a computation expression: `taskSeq { ... }` with an accompanying `TaskSeq` module, that allows seamless use of asynchronous sequences similar to F#'s native `seq` and `task` CE's.
+
+This readme covers the highlights and a summary of implemented functions.
+A more extensive overview can be found in the [repository's readme][1].
-----------------------------------------
@@ -56,7 +59,7 @@ let helloTs = taskSeq { yield "Hello, World!" }
let f() = task {
// using toList forces execution of whole sequence
let! hello = TaskSeq.toList helloTs // toList returns a Task<'T list>
- return List.head hello
+ return List.head hello
}
// can be mixed with normal sequences
@@ -76,15 +79,15 @@ let allFilesAsLines() = taskSeq {
yield! contents
}
-let write file =
+let write file =
allFilesAsLines()
-
+
// synchronous map function on asynchronous task sequence
|> TaskSeq.map (fun x -> x.Replace("a", "b"))
// asynchronous map
|> TaskSeq.mapAsync (fun x -> task { return "hello: " + x })
-
+
// asynchronous iter
|> TaskSeq.iterAsync (fun data -> File.WriteAllTextAsync(fileName, data))
@@ -102,144 +105,149 @@ let feedFromTwitter user pwd = taskSeq {
We are working hard on getting a full set of module functions on `TaskSeq` that can be used with `IAsyncEnumerable` sequences. Our guide is the set of F# `Seq` functions in F# Core and, where applicable, the functions provided from `AsyncSeq`. Each implemented function is documented through XML doc comments to provide the necessary context-sensitive help.
-The following is the progress report:
-
-| Done | `Seq` | `TaskSeq` | Variants | Remarks |
-|------------------|--------------------|-----------------|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| ❓ | `allPairs` | `allPairs` | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ✅ [#81][] | `append` | `append` | | |
-| ✅ [#81][] | | | `appendSeq` | |
-| ✅ [#81][] | | | `prependSeq` | |
-| | `average` | `average` | | |
-| | `averageBy` | `averageBy` | `averageByAsync` | |
-| ❓ | `cache` | `cache` | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ✅ [#67][] | `cast` | `cast` | | |
-| ✅ [#67][] | | | `box` | |
-| ✅ [#67][] | | | `unbox` | |
-| ✅ [#23][] | `choose` | `choose` | `chooseAsync` | |
-| | `chunkBySize` | `chunkBySize` | | |
-| ✅ [#11][] | `collect` | `collect` | `collectAsync` | |
-| ✅ [#11][] | | `collectSeq` | `collectSeqAsync` | |
-| | `compareWith` | `compareWith` | `compareWithAsync` | |
-| ✅ [#69][] | `concat` | `concat` | | |
-| ✅ [#70][] | `contains` | `contains` | | |
-| ✅ [#82][] | `delay` | `delay` | | |
-| | `distinct` | `distinct` | | |
-| | `distinctBy` | `dictinctBy` | `distinctByAsync` | |
-| ✅ [#2][] | `empty` | `empty` | | |
-| ✅ [#23][] | `exactlyOne` | `exactlyOne` | | |
-| ✅ [#83][] | `except` | `except` | | |
-| ✅ [#83][] | | `exceptOfSeq` | | |
-| ✅ [#70][] | `exists` | `exists` | `existsAsync` | |
-| | `exists2` | `exists2` | | |
-| ✅ [#23][] | `filter` | `filter` | `filterAsync` | |
-| ✅ [#23][] | `find` | `find` | `findAsync` | |
-| 🚫 | `findBack` | | | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#68][] | `findIndex` | `findIndex` | `findIndexAsync` | |
-| 🚫 | `findIndexBack` | n/a | n/a | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#2][] | `fold` | `fold` | `foldAsync` | |
-| | `fold2` | `fold2` | `fold2Async` | |
-| 🚫 | `foldBack` | | | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| 🚫 | `foldBack2` | | | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| | `forall` | `forall` | `forallAsync` | |
-| | `forall2` | `forall2` | `forall2Async` | |
-| ❓ | `groupBy` | `groupBy` | `groupByAsync` | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ✅ [#23][] | `head` | `head` | | |
-| ✅ [#68][] | `indexed` | `indexed` | | |
-| ✅ [#69][] | `init` | `init` | `initAsync` | |
-| ✅ [#69][] | `initInfinite` | `initInfinite` | `initInfiniteAsync` | |
-| | `insertAt` | `insertAt` | | |
-| | `insertManyAt` | `insertManyAt` | | |
-| ✅ [#23][] | `isEmpty` | `isEmpty` | | |
-| ✅ [#23][] | `item` | `item` | | |
-| ✅ [#2][] | `iter` | `iter` | `iterAsync` | |
-| | `iter2` | `iter2` | `iter2Async` | |
-| ✅ [#2][] | `iteri` | `iteri` | `iteriAsync` | |
-| | `iteri2` | `iteri2` | `iteri2Async` | |
-| ✅ [#23][] | `last` | `last` | | |
-| ✅ [#53][] | `length` | `length` | | |
-| ✅ [#53][] | | `lengthBy` | `lengthByAsync` | |
-| ✅ [#2][] | `map` | `map` | `mapAsync` | |
-| | `map2` | `map2` | `map2Async` | |
-| | `map3` | `map3` | `map3Async` | |
-| | `mapFold` | `mapFold` | `mapFoldAsync` | |
-| 🚫 | `mapFoldBack` | | | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#2][] | `mapi` | `mapi` | `mapiAsync` | |
-| | `mapi2` | `mapi2` | `mapi2Async` | |
-| | `max` | `max` | | |
-| | `maxBy` | `maxBy` | `maxByAsync` | |
-| | `min` | `min` | | |
-| | `minBy` | `minBy` | `minByAsync` | |
-| ✅ [#2][] | `ofArray` | `ofArray` | | |
-| ✅ [#2][] | | `ofAsyncArray` | | |
-| ✅ [#2][] | | `ofAsyncList` | | |
-| ✅ [#2][] | | `ofAsyncSeq` | | |
-| ✅ [#2][] | `ofList` | `ofList` | | |
-| ✅ [#2][] | | `ofTaskList` | | |
-| ✅ [#2][] | | `ofResizeArray` | | |
-| ✅ [#2][] | | `ofSeq` | | |
-| ✅ [#2][] | | `ofTaskArray` | | |
-| ✅ [#2][] | | `ofTaskList` | | |
-| ✅ [#2][] | | `ofTaskSeq` | | |
-| | `pairwise` | `pairwise` | | |
-| | `permute` | `permute` | `permuteAsync` | |
-| ✅ [#23][] | `pick` | `pick` | `pickAsync` | |
-| 🚫 | `readOnly` | | | [note #3](#note-3 "The motivation for 'readOnly' in 'Seq' is that a cast from a mutable array or list to a 'seq<_>' is valid and can be cast back, leading to a mutable sequence. Since 'TaskSeq' doesn't implement 'IEnumerable<_>', such casts are not possible.") |
-| | `reduce` | `reduce` | `reduceAsync` | |
-| 🚫 | `reduceBack` | | | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| | `removeAt` | `removeAt` | | |
-| | `removeManyAt` | `removeManyAt` | | |
-| | `replicate` | `replicate` | | |
-| ❓ | `rev` | | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| | `scan` | `scan` | `scanAsync` | |
-| 🚫 | `scanBack` | | | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#90][] | `singleton` | `singleton` | | |
-| | `skip` | `skip` | | |
-| | `skipWhile` | `skipWhile` | `skipWhileAsync` | |
-| ❓ | `sort` | | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ❓ | `sortBy` | | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ❓ | `sortByAscending` | | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ❓ | `sortByDescending` | | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| ❓ | `sortWith` | | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| | `splitInto` | `splitInto` | | |
-| | `sum` | `sum` | | |
-| | `sumBy` | `sumBy` | `sumByAsync` | |
-| ✅ [#76][] | `tail` | `tail` | | |
-| | `take` | `take` | | |
-| | `takeWhile` | `takeWhile` | `takeWhileAsync` | |
-| ✅ [#2][] | `toArray` | `toArray` | `toArrayAsync` | |
-| ✅ [#2][] | | `toIList` | `toIListAsync` | |
-| ✅ [#2][] | `toList` | `toList` | `toListAsync` | |
-| ✅ [#2][] | | `toResizeArray` | `toResizeArrayAsync` | |
-| ✅ [#2][] | | `toSeq` | `toSeqAsync` | |
-| | | […] | | |
-| ❓ | `transpose` | | | [note #1](#note-1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
-| | `truncate` | `truncate` | | |
-| ✅ [#23][] | `tryExactlyOne` | `tryExactlyOne` | `tryExactlyOneAsync` | |
-| ✅ [#23][] | `tryFind` | `tryFind` | `tryFindAsync` | |
-| 🚫 | `tryFindBack` | | | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#68][] | `tryFindIndex` | `tryFindIndex` | `tryFindIndexAsync` | |
-| 🚫 | `tryFindIndexBack` | | | [note #2](#note-2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
-| ✅ [#23][] | `tryHead` | `tryHead` | | |
-| ✅ [#23][] | `tryItem` | `tryItem` | | |
-| ✅ [#23][] | `tryLast` | `tryLast` | | |
-| ✅ [#23][] | `tryPick` | `tryPick` | `tryPickAsync` | |
-| ✅ [#76][] | | `tryTail` | | |
-| | `unfold` | `unfold` | `unfoldAsync` | |
-| | `updateAt` | `updateAt` | | |
-| | `where` | `where` | `whereAsync` | |
-| | `windowed` | `windowed` | | |
-| ✅ [#2][] | `zip` | `zip` | | |
-| | `zip3` | `zip3` | | |
-| | | `zip4` | | |
+We are working hard on getting a full set of module functions on `TaskSeq` that can be used with `IAsyncEnumerable` sequences. Our guide is the set of F# `Seq` functions in F# Core and, where applicable, the functions provided by `AsyncSeq`. Each implemented function is documented through XML doc comments to provide the necessary context-sensitive help.
+
+This is what was implemented, planned or skipped:
+
+| Done | `Seq` | `TaskSeq` | Variants | Remarks |
+|------------------|--------------------|----------------------|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| ❓ | `allPairs` | `allPairs` | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ✅ [#81][] | `append` | `append` | | |
+| ✅ [#81][] | | | `appendSeq` | |
+| ✅ [#81][] | | | `prependSeq` | |
+| | `average` | `average` | | |
+| | `averageBy` | `averageBy` | `averageByAsync` | |
+| ❓ | `cache` | `cache` | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ✅ [#67][] | `cast` | `cast` | | |
+| ✅ [#67][] | | | `box` | |
+| ✅ [#67][] | | | `unbox` | |
+| ✅ [#23][] | `choose` | `choose` | `chooseAsync` | |
+| | `chunkBySize` | `chunkBySize` | | |
+| ✅ [#11][] | `collect` | `collect` | `collectAsync` | |
+| ✅ [#11][] | | `collectSeq` | `collectSeqAsync` | |
+| | `compareWith` | `compareWith` | `compareWithAsync` | |
+| ✅ [#69][] | `concat` | `concat` | | |
+| ✅ [#70][] | `contains` | `contains` | | |
+| ✅ [#82][] | `delay` | `delay` | | |
+| | `distinct` | `distinct` | | |
+| | `distinctBy` | `dictinctBy` | `distinctByAsync` | |
+| ✅ [#2][] | `empty` | `empty` | | |
+| ✅ [#23][] | `exactlyOne` | `exactlyOne` | | |
+| ✅ [#83][] | `except` | `except` | | |
+| ✅ [#83][] | | `exceptOfSeq` | | |
+| ✅ [#70][] | `exists` | `exists` | `existsAsync` | |
+| | `exists2` | `exists2` | | |
+| ✅ [#23][] | `filter` | `filter` | `filterAsync` | |
+| ✅ [#23][] | `find` | `find` | `findAsync` | |
+| 🚫 | `findBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#68][] | `findIndex` | `findIndex` | `findIndexAsync` | |
+| 🚫 | `findIndexBack` | n/a | n/a | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#2][] | `fold` | `fold` | `foldAsync` | |
+| | `fold2` | `fold2` | `fold2Async` | |
+| 🚫 | `foldBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| 🚫 | `foldBack2` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| | `forall` | `forall` | `forallAsync` | |
+| | `forall2` | `forall2` | `forall2Async` | |
+| ❓ | `groupBy` | `groupBy` | `groupByAsync` | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ✅ [#23][] | `head` | `head` | | |
+| ✅ [#68][] | `indexed` | `indexed` | | |
+| ✅ [#69][] | `init` | `init` | `initAsync` | |
+| ✅ [#69][] | `initInfinite` | `initInfinite` | `initInfiniteAsync` | |
+| | `insertAt` | `insertAt` | | |
+| | `insertManyAt` | `insertManyAt` | | |
+| ✅ [#23][] | `isEmpty` | `isEmpty` | | |
+| ✅ [#23][] | `item` | `item` | | |
+| ✅ [#2][] | `iter` | `iter` | `iterAsync` | |
+| | `iter2` | `iter2` | `iter2Async` | |
+| ✅ [#2][] | `iteri` | `iteri` | `iteriAsync` | |
+| | `iteri2` | `iteri2` | `iteri2Async` | |
+| ✅ [#23][] | `last` | `last` | | |
+| ✅ [#53][] | `length` | `length` | | |
+| ✅ [#53][] | | `lengthBy` | `lengthByAsync` | |
+| ✅ [#2][] | `map` | `map` | `mapAsync` | |
+| | `map2` | `map2` | `map2Async` | |
+| | `map3` | `map3` | `map3Async` | |
+| | `mapFold` | `mapFold` | `mapFoldAsync` | |
+| 🚫 | `mapFoldBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#2][] | `mapi` | `mapi` | `mapiAsync` | |
+| | `mapi2` | `mapi2` | `mapi2Async` | |
+| | `max` | `max` | | |
+| | `maxBy` | `maxBy` | `maxByAsync` | |
+| | `min` | `min` | | |
+| | `minBy` | `minBy` | `minByAsync` | |
+| ✅ [#2][] | `ofArray` | `ofArray` | | |
+| ✅ [#2][] | | `ofAsyncArray` | | |
+| ✅ [#2][] | | `ofAsyncList` | | |
+| ✅ [#2][] | | `ofAsyncSeq` | | |
+| ✅ [#2][] | `ofList` | `ofList` | | |
+| ✅ [#2][] | | `ofTaskList` | | |
+| ✅ [#2][] | | `ofResizeArray` | | |
+| ✅ [#2][] | | `ofSeq` | | |
+| ✅ [#2][] | | `ofTaskArray` | | |
+| ✅ [#2][] | | `ofTaskList` | | |
+| ✅ [#2][] | | `ofTaskSeq` | | |
+| | `pairwise` | `pairwise` | | |
+| | `permute` | `permute` | `permuteAsync` | |
+| ✅ [#23][] | `pick` | `pick` | `pickAsync` | |
+| 🚫 | `readOnly` | | | [note #3](#note3 "The motivation for 'readOnly' in 'Seq' is that a cast from a mutable array or list to a 'seq<_>' is valid and can be cast back, leading to a mutable sequence. Since 'TaskSeq' doesn't implement 'IEnumerable<_>', such casts are not possible.") |
+| | `reduce` | `reduce` | `reduceAsync` | |
+| 🚫 | `reduceBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| | `removeAt` | `removeAt` | | |
+| | `removeManyAt` | `removeManyAt` | | |
+| | `replicate` | `replicate` | | |
+| ❓ | `rev` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| | `scan` | `scan` | `scanAsync` | |
+| 🚫 | `scanBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#90][] | `singleton` | `singleton` | | |
+| ✅ [#209][]| `skip` | `skip` | | |
+| ✅ [#209][]| | `drop` | | |
+| | `skipWhile` | `skipWhile` | `skipWhileAsync` | |
+| | | `skipWhileInclusive` | `skipWhileInclusiveAsync` | |
+| ❓ | `sort` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ❓ | `sortBy` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ❓ | `sortByAscending` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ❓ | `sortByDescending` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ❓ | `sortWith` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| | `splitInto` | `splitInto` | | |
+| | `sum` | `sum` | | |
+| | `sumBy` | `sumBy` | `sumByAsync` | |
+| ✅ [#76][] | `tail` | `tail` | | |
+| ✅ [#209][]| `take` | `take` | | |
+| ✅ [#126][]| `takeWhile` | `takeWhile` | `takeWhileAsync` | |
+| ✅ [#126][]| | `takeWhileInclusive` | `takeWhileInclusiveAsync` | |
+| ✅ [#2][] | `toArray` | `toArray` | `toArrayAsync` | |
+| ✅ [#2][] | | `toIList` | `toIListAsync` | |
+| ✅ [#2][] | `toList` | `toList` | `toListAsync` | |
+| ✅ [#2][] | | `toResizeArray` | `toResizeArrayAsync` | |
+| ✅ [#2][] | | `toSeq` | `toSeqAsync` | |
+| | | […] | | |
+| ❓ | `transpose` | | | [note #1](#note1 "These functions require a form of pre-materializing through 'TaskSeq.cache', similar to the approach taken in the corresponding 'Seq' functions. It doesn't make much sense to have a cached async sequence. However, 'AsyncSeq' does implement these, so we'll probably do so eventually as well.") |
+| ✅ [#209][]| `truncate` | `truncate` | | |
+| ✅ [#23][] | `tryExactlyOne` | `tryExactlyOne` | `tryExactlyOneAsync` | |
+| ✅ [#23][] | `tryFind` | `tryFind` | `tryFindAsync` | |
+| 🚫 | `tryFindBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#68][] | `tryFindIndex` | `tryFindIndex` | `tryFindIndexAsync` | |
+| 🚫 | `tryFindIndexBack` | | | [note #2](#note2 "Because of the async nature of TaskSeq sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the 'Back' iterators.") |
+| ✅ [#23][] | `tryHead` | `tryHead` | | |
+| ✅ [#23][] | `tryItem` | `tryItem` | | |
+| ✅ [#23][] | `tryLast` | `tryLast` | | |
+| ✅ [#23][] | `tryPick` | `tryPick` | `tryPickAsync` | |
+| ✅ [#76][] | | `tryTail` | | |
+| | `unfold` | `unfold` | `unfoldAsync` | |
+| | `updateAt` | `updateAt` | | |
+| | `where` | `where` | `whereAsync` | |
+| | `windowed` | `windowed` | | |
+| ✅ [#2][] | `zip` | `zip` | | |
+| | `zip3` | `zip3` | | |
+| | | `zip4` | | |
#### Note 1
-_These functions require a form of pre-materializing through `TaskSeq.cache`, similar to the approach taken in the corresponding `Seq` functions. It doesn't make much sense to have a cached async sequence. However, `AsyncSeq` does implement these, so we'll probably do so eventually as well._
+_These functions require a form of pre-materializing through `TaskSeq.cache`, similar to the approach taken in the corresponding `Seq` functions. It doesn't make much sense to have a cached async sequence. However, `AsyncSeq` does implement these, so we'll probably do so eventually as well._
#### Note 2
-_Because of the async nature of `TaskSeq` sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the `xxxBack` iterators._
+_Because of the async nature of `TaskSeq` sequences, iterating from the back would be bad practice. Instead, materialize the sequence to a list or array and then apply the `xxxBack` iterators._
#### Note 3
@@ -267,6 +275,7 @@ _The motivation for `readOnly` in `Seq` is that a cast from a mutable array or l
- [Docs on MSDN][18] form a good summary and starting point.
- Arguably the best [step-by-step tutorial to using and building computation expressions][19] by Scott Wlaschin.
+[1]: https://github.com/fsprojects/FSharp.Control.TaskSeq#taskseq
[3]: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1?view=net-7.0
[4]: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerator-1.movenextasync?view=net-7.0
[5]: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerator-1?view=net-7.0
@@ -297,4 +306,6 @@ _The motivation for `readOnly` in `Seq` is that a cast from a mutable array or l
[#81]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/81
[#82]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/82
[#83]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/83
-[#90]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/90
\ No newline at end of file
+[#90]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/90
+[#126]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/126
+[#209]: https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/209
\ No newline at end of file
diff --git a/release-notes.txt b/release-notes.txt
index 8d13122e..ebd40c4c 100644
--- a/release-notes.txt
+++ b/release-notes.txt
@@ -1,10 +1,14 @@
Release notes:
0.4.x (unreleased)
- - overhaul all doc comments, add exceptions, improve editor experience, #136
+ - overhaul all doc comments, add exceptions, improve IDE quick-info experience, #136
+ - new surface area functions, fixes #208:
+ * TaskSeq.take, TaskSeq.skip, #209
+ * TaskSeq.truncate, TaskSeq.drop, #209
+
- Performance: less thread hops with 'StartImmediateAsTask' instead of 'StartAsTask', fixes #135
- BINARY INCOMPATIBILITY: 'TaskSeq' module is now static members on 'TaskSeq<_>', fixes #184
- - DEPRECATIONS (these raise warning FS0044):
+ - DEPRECATIONS (warning FS0044):
- type 'taskSeq<_>' is renamed to 'TaskSeq<_>', fixes #193
- function 'ValueTask.ofIValueTaskSource` renamed to `ValueTask.ofSource`, fixes #193
- function `ValueTask.FromResult` is renamed to `ValueTask.fromResult`, fixes #193
diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi
index 3c14581d..d9de462d 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi
+++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi
@@ -728,8 +728,8 @@ type TaskSeq =
///
/// Returns a task sequence that, when iterated, skips elements of the underlying
/// sequence, and then yields the remainder. Raises an exception if there are not
- /// items. See for a version that does not raise an exception.
- /// See also for the inverse of this operation.
+ /// items. See for a version that does not raise an exception.
+ /// See also for the inverse of this operation.
///
///
/// The number of items to skip.
@@ -746,8 +746,8 @@ type TaskSeq =
///
/// Returns a task sequence that, when iterated, drops at most elements of the
/// underlying sequence, and then returns the remainder of the elements, if any.
- /// See for a version that raises an exception if there
- /// are not enough elements. See also for the inverse of this operation.
+ /// See for a version that raises an exception if there
+ /// are not enough elements. See also for the inverse of this operation.
///
///
/// The maximum number of items to drop.
@@ -760,8 +760,8 @@ type TaskSeq =
///
/// Returns a task sequence that, when iterated, yields elements of the
/// underlying sequence, and then returns no further elements. Raises an exception if there are not enough
- /// elements in the sequence. See for a version that does not raise an exception.
- /// See also for the inverse of this operation.
+ /// elements in the sequence. See for a version that does not raise an exception.
+ /// See also for the inverse of this operation.
///
///
/// The number of items to take.
@@ -777,8 +777,8 @@ type TaskSeq =
///
/// Returns a task sequence that, when iterated, yields at most elements of the underlying
/// sequence, truncating the remainder, if any.
- /// See for a version that raises an exception if there are not enough elements in the
- /// sequence. See also for the inverse of this operation.
+ /// See for a version that raises an exception if there are not enough elements in the
+ /// sequence. See also for the inverse of this operation.
///
///
/// The maximum number of items to enumerate.