FSharp.Control.TaskSeq 0.4.0

dotnet add package FSharp.Control.TaskSeq --version 0.4.0                
NuGet\Install-Package FSharp.Control.TaskSeq -Version 0.4.0                
此命令旨在在 Visual Studio 的包管理器控制台中使用,因为它使用 NuGet 模块的 Install-Package 版本。
<PackageReference Include="FSharp.Control.TaskSeq" Version="0.4.0" />                
对于支持 PackageReference 的项目,将此 XML 节复制到项目文件中以引用包。
paket add FSharp.Control.TaskSeq --version 0.4.0                
#r "nuget: FSharp.Control.TaskSeq, 0.4.0"                
#r 指令可用于 F# Interactive 和多语言笔记本。将其复制到交互式工具或脚本的源代码中以引用包。
// Install FSharp.Control.TaskSeq as a Cake Addin
#addin nuget:?package=FSharp.Control.TaskSeq&version=0.4.0

// Install FSharp.Control.TaskSeq as a Cake Tool
#tool nuget:?package=FSharp.Control.TaskSeq&version=0.4.0                

TaskSeq

IAsyncEnumerable 的实现,作为计算表达式:带有关联的 TaskSeq 模块和函数的 taskSeq { ... },允许无缝使用与 F# 原生 seqtask CE 类似的异步序列。

此README介绍了一些重点和已实现函数的摘要。更详细的内容可以在存储库的README中找到。


目录


概述

.NET 在 .NET Core 3.0 中添加了 IAsyncEnumerable 接口,它是 .NET Standard 2.1 的一部分。主要用例是针对某些资源的迭代异步、顺序枚举。例如,一个事件流或分页的 REST API 接口,或异步读取文件列表并累积结果,其中的每个动作可以建模为对由 MoveNextAsync 调用返回的 IAsyncEnumerator<'T> 的调用,而该调用是通过 GetAsyncEnumerator() 调用获得的。

自 F# 中引入 task 以来,对原生 任务序列 实现的需求不断增长,特别是在需要对 IAsyncEnumerable 进行正确迭代的情况下,尤其是当想要避免使用可变变量时。这个库是对这一需求的回应,并使用 taskSeq 实现了相同的 可恢复状态机 方法。

模块函数

seqSeq 一样,这个库提供了一堆熟悉收集函数,如 TaskSeq.emptyisEmptyTaskSeq.mapitercollectfoldTaskSeq.findpickchoosefilter。在这些函数适用的情况下,它们还包括异步版本,如 TaskSeq.mapAsynciterAsynccollectAsyncfoldAsyncTaskSeq.findAsyncpickAsyncchooseAsyncfilterAsync,这使得应用的功能可以是异步的。

有关当前实现的函数及其变体的完整列表,请参阅下文。

taskSeq 计算表达式

可以使用 taskSeq 计算表达式就像使用 seq 一样。除此之外,它还添加了对通过 let! 和循环遍历正常或异步序列(实现 IAsyncEnumerable<'T> 的序列)进行处理的支持。您可以使用 yield!yield,并且对任务序列表达式内的 useuse!try-withtry-finally 以及 while 循环提供了支持。

示例

open System.IO
open FSharp.Control

// singleton is fine
let helloTs = taskSeq { yield "Hello, World!" }

// cold-started, that is, delay-executed
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
}

// can be mixed with normal sequences
let oneToTen = taskSeq { yield! [1..10] }

// can be used with F#'s task and async in a for-loop
let f() = task { for x in oneToTen do printfn "Number %i" x }
let g() = async { for x in oneToTen do printfn "Number %i" x }

// returns a delayed sequence of IAsyncEnumerable<string>
let allFilesAsLines() = taskSeq {
    let files = Directory.EnumerateFiles(@"c:\temp")
    for file in files do
        // await
        let! contents = File.ReadAllLinesAsync file
        // return all lines
        yield! contents
}

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))


// infinite sequence
let feedFromTwitter user pwd = taskSeq {
    do! loginToTwitterAsync(user, pwd)
    while true do
       let! message = getNextNextTwitterMessageAsync()
       yield message
}

TaskSeq 模块函数

我们正在努力在 TaskSeq 上得到一组完整的模块函数,以便与 IAsyncEnumerable 序列一起使用。我们的指导是 F# 核心中 Seq 函数的集合,以及适用时 AsyncSeq 提供的函数。每个实现的函数都通过 XML 文档注释进行了文档化,以提供必要的上下文相关的帮助。

以下是至今为止已实现、计划实现或跳过的内容。

已完成 Seq TaskSeq 变体 备注
allPairs allPairs 笔记 #1
#81 append append
#81 appendSeq
#81 prependSeq
average average
averageBy averageBy averageByAsync
cache cache 笔记 #1
#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
#237 concat (列表) concat (列表)
#237 concat (数组) concat (数组)
#237 concat (r-array) concat (r-array)
#237 concat (seq) concat (seq)
#70 contains contains
#82 延迟 延迟
distinct distinct
distinctBy dictinctBy (此处有一个拼写错误,可能为distinctBy) distinctByAsync
#209 drop
#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
#68 findIndex findIndex findIndexAsync
🚫 findIndexBack n/a n/a note #2
#2 fold fold foldAsync
fold2 fold2 fold2Async
🚫 foldBack note #2
🚫 foldBack2 note #2
#240 forall forall forallAsync
forall2 forall2 forall2Async
groupBy groupBy groupByAsync 笔记 #1
#23 head head
#68 indexed indexed
#69 init init initAsync
#69 initInfinite initInfinite initInfiniteAsync
#236 insertAt insertAt
#236 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
#2 mapi mapi mapiAsync
mapi2 mapi2 mapi2Async
#221 max max
#221 maxBy maxBy maxByAsync
#221 min min
#221 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
reduce reduce reduceAsync
🚫 reduceBack note #2
#236 removeAt removeAt
#236 removeManyAt removeManyAt
replicate replicate
rev 笔记 #1
scan scan scanAsync
🚫 scanBack note #2
#90 singleton singleton
#209 skip skip
#219 skipWhile skipWhile skipWhileAsync
#219 skipWhileInclusive skipWhileInclusiveAsync
sort 笔记 #1
sortBy 笔记 #1
sortByAscending 笔记 #1
sortByDescending 笔记 #1
sortWith 笔记 #1
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 笔记 #1
#209 truncate truncate
#23 tryExactlyOne tryExactlyOne tryExactlyOneAsync
#23 tryFind tryFind tryFindAsync
🚫 tryFindBack note #2
#68 tryFindIndex tryFindIndex tryFindIndexAsync
🚫 tryFindIndexBack note #2
#23 tryHead tryHead
#23 tryItem tryItem
#23 tryLast tryLast
#23 tryPick tryPick tryPickAsync
#76 tryTail
unfold unfold unfoldAsync
#236 updateAt updateAt
#217 where where whereAsync
windowed windowed
#2 zip zip
zip3 zip3
zip4
Note 1

这些函数要求通过 TaskSeq.cache 进行某种形式的预先验证,类似于在相应的 Seq 函数中采用的方法。拥有一个缓存的后台异步序列没有太多意义。但是,AsyncSeq 仍然实现了这些功能,所以我们可能会最终这样做。

备注2

由于 TaskSeq 序列的异步本质,从后往前迭代是不好的做法。相反,应将序列材料化为列表或数组,然后应用 xxxBack 迭代器。

备注3

Seq 中引入 readOnly 的动机是,从可变的数组或列表到 seq<_> 的转换是有效的,并且可以转换回,从而导致可变序列。由于 TaskSeq 不实现 IEnumerable<_>,因此这些转换是不可能的。

更多信息

进一步阅读 IAsyncEnumerable

  • 有关如何使用 C# 的良好介绍,请参阅此博客 https://stu.dev/iasyncenumerable-introduction/
  • 在此之后不久,《MSDN 文章》介绍了异步可枚举。
  • 以下是一个示例,说明如何将 seq 转换为 IAsyncEnumerable https://gist.github.com/akhansari/d88812b742aa6be1c35b4f46bd9f8532,尽管 TaskSeq 包含许多更多实用功能,且采用的方法略有不同。
  • 如果您想使用 IAsyncEnumerable 并使用 async 而不是 task,则应使用出色的 AsyncSeq 库。虽然 TaskSeq 的目的是像 task 一样消费 async,但它不会创建一个 AsyncSeq 类型(至少目前还没有)。如果您想使用经典异步和并行处理,则应使用此库。

关于可恢复状态机的进一步阅读

关于计算表达式的进一步阅读

产品 兼容的和额外的计算目标框架版本。
.NET net5.0 已计算。 net5.0-windows 已计算。 net6.0 已计算。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 已计算。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。
.NET Core netcoreapp3.0 已计算。 netcoreapp3.1 已计算。
.NET Standard netstandard2.1 兼容。
MonoAndroid monoandroid 已计算。
MonoMac monomac 已计算。
MonoTouch monotouch 已计算。
Tizen tizen60 已计算。
Xamarin.iOS xamarinios 已计算。
Xamarin.Mac xamarinmac 已计算。
Xamarin.TVOS xamarintvos 已计算。
Xamarin.WatchOS xamarinwatchos 已计算。
兼容的目标框架
包含的目标框架(在包中)
了解关于目标框架.NET标准的更多信息。

NuGet包 (10)

显示依赖FSharp.Control.TaskSeq的前5个NuGet包

下载
Equinox.CosmosStore

高效的源事件决策和数据

Equinox.EventStore

高效的源事件决策和数据

Propulsion.Feed

高效的事件流管道

Equinox.DynamoStore

高效的源事件决策和数据

AzureTackle

AzureTackle的F# API,实现高效访问AzureTackle数据库,并添加了函数式调味。

GitHub存储库

此包未在任何流行的GitHub存储库中使用。

版本 下载 最后更新
0.4.0 29,065 3/17/2024
0.4.0-alpha.1 47,840 6/5/2023
0.3.0 107,288 11/28/2022
0.2.2 1,550 11/10/2022
0.2.1 661 11/10/2022
0.2.0 345 11/9/2022
0.1.1 332 11/9/2022
0.1.0 417 11/9/2022

发行说明
0.4.0
   - 重构所有文档注释,添加异常,改善IDE快速信息体验,#136,#220,#234
   - 新颖的表面函数,修复 #208
     * TaskSeq.take, skip, #209
     * TaskSeq.truncate, drop, #209
     * TaskSeq.where, whereAsync, #217
     * TaskSeq.skipWhile, skipWhileInclusive, skipWhileAsync, skipWhileInclusiveAsync, #219
     * TaskSeq.max, min, maxBy, minBy, maxByAsync, minByAsync, #221
     * TaskSeq.insertAt, insertManyAt, removeAt, removeManyAt, updateAt, #236
     * TaskSeq.forall, forallAsync, #240
     * TaskSeq.concat (覆盖:seq, array, resizearray, list), #237

   - 性能:用 'StartImmediateAsTask' 替代 'StartAsTask' 以减少线程跳跃,修复 #135
   - 性能:几个内联和分配改进
   - BINARY INCOMPATIBILITY:'TaskSeq' 模块被 'TaskSeq<_>' 上的静态成员替换,修复 #184
   - DEPRECATIONS(警告 FS0044)
     - 类型 'taskSeq<_>' 重命名为 'TaskSeq<_>', 修复 #193
     - 函数 'ValueTask.ofIValueTaskSource` 重命名为 `ValueTask.ofSource', 修复 #193
     - 函数 `ValueTask.FromResult` 重命名为 `ValueTask.fromResult', 修复 #193

0.4.0-alpha.1
   - bugfix:对于 'use!'、'use' 或 `finally` 块,不调用 Dispose,#157(由 @bartelink)
   - BREAKING CHANGE:null 参数现在引发 ArgumentNullException,而不是 NullReferenceException,#127
   - 添加对F#的 Async<'T> 的 `let!` 和 `do!` 支持,#79,#114
   - 添加 TaskSeq.takeWhile, takeWhileAsync, takeWhileInclusive, takeWhileInclusiveAsync,#126(由 @bartelink)
   - 添加 AsyncSeq与TaskSeq比较图,#131
   - bugfix:从文件依赖中删除 release-notes.txt,但保留在包中,#138

0.3.0
   - 改进xml文档注释,命名文件以公开类型,修复 #112。
   - 添加对静态 TaskLike 的支持,允许使用与F#任务相同的 let! 和 do! 载重,修复 #110。
   - 实现 'do!' 以支持非泛型的 Task,如 Task.Delay,修复 #43。
   - task和async CEs用对 'for .. in ..do' 的支持扩展,#75,#93,#99(部分由 @theangrybyrd)
   - 添加 TaskSeq.singleton,#90(由 @gusty)
   - bugfix:修复与 'use' 和 'use!' 相关的重载解析错误,#97(感谢 @peterfaria)
   - 改进 TaskSeq.empty,不依赖于可恢复状态,#89(由 @gusty)
   - 修复了 bug:在 TaskSeq.zip 中不再因为长度不等而抛出异常,修复了 #32 问题。
   - 兼容性退步:几个仅内部使用类型现在已隐藏

0.2.2
   - 删除了错误命名的 TaskSeq.toSeqCachedAsync。请使用 toSeq 或 toListAsync。
   - 将 TaskSeq.toSeqCached 重命名为 TaskSeq.toSeq,这是其实际的工作行为。

0.2.1
   - 修复了完成迭代中的 ValueTask 的问题。
   - 添加了 `TaskSeq.except` 和 `TaskSeq.exceptOfSeq` 异步操作。

0.2
   - 从 .NET 6.0 移动到 NetStandard 2.1 以实现更大的兼容性,无功能变更。
   - 移动到最小必要的 FSharp.Core 版本:6.0.2。
   - 更新了README中的进度概述,纠正了元信息,并添加了发布说明。

0.1.1
   - 更新了 nuggest 包中的元信息并添加了 README。

0.1
   - 首次发布
   - 使用可恢复的状态机实现 taskSeq CE
      - 支持:yield,yield!,let,let!,while,for,try-with,try-finally,use,use!
      - 以及:任务和值任务
   - 添加了 toXXX / ofXXX 函数
   - 添加了 map/mapi/fold/iter/iteri/collect 等,并带有异步版本
   - 添加了 find/pick/choose/filter 等,并带有异步版本和 'try' 变体
   - 添加了 cast/concat/append/prepend/delay/exactlyOne
   - 添加了 empty/isEmpty
   - 添加了 findIndex/indexed/init/initInfinite
   - 添加了 head/last/tryHead/tryLast/tail/tryTail
   - 添加了 zip/length