f# - 如何实现Task.Map

标签 f# c#-to-f#

我是否正确地为 Task 实现了 map

let map continuation (t: Task<'A>) =
    t.ContinueWith(fun (antecedent: Task<'A>) ->
        if  antecedent.Status <> TaskStatus.Canceled &&
            antecedent.Status <> TaskStatus.Faulted then
            continuation antecedent.Result
        else
            raise antecedent.Exception // must I?
    )

我从 the docs 得到了 TaskStatus 检查.我对 raise antecedent.Exception 感到最不确定,但我想不出另一种方法来处理它。


作为背景,是的,我知道 Async,但我当前的堆栈使用 Entity Framework 和 Blazor,所以我有一个后端使用 .ToListAsync() > 和 C# 中的前端,所以我宁愿不处理从 TaskAsync 然后再返回的转换。

最佳答案

我建议根据 TPL 中 awaitable 概念背后的接口(interface)来实现您的解决方案,即 INotifyCompletionICriticalNotifyCompletion。此外,要正确实现 map,您应该真正按照 bind 来实现。这在 F# 中已经有一些现有的解决方案,例如 TaskBuilder图书馆。就个人而言,多年来我一直在图书馆中使用以下内容,没有任何问题:

open System.Runtime.CompilerServices
open System.Threading.Tasks

type TaskStep<'result> =
| Value of 'result
| AsyncValue of 'result Task
| Continuation of ICriticalNotifyCompletion * (unit -> 'result TaskStep)
and StateMachine<'a>(firstStep) as this =
    let methodBuilder = AsyncTaskMethodBuilder<'a Task>()
    let mutable continuation = fun () -> firstStep
    let nextAwaitable() =
        try
            match continuation() with
            | Value r ->
                methodBuilder.SetResult(Task.FromResult(r))
                null
            | AsyncValue t ->
                methodBuilder.SetResult(t)
                null
            | Continuation (await, next) ->
                continuation <- next
                await
        with
        | exn ->
            methodBuilder.SetException(exn)
            null
    let mutable self = this

    member __.Run() =
        methodBuilder.Start(&self)
        methodBuilder.Task

    interface IAsyncStateMachine with
        member __.MoveNext() =
            let mutable await = nextAwaitable()
            if not (isNull await) then
                methodBuilder.AwaitUnsafeOnCompleted(&await, &self)    
        member __.SetStateMachine(_) = 
            () 

type Binder<'out> =
    static member inline GenericAwait< ^abl, ^awt, ^inp
                                        when ^abl : (member GetAwaiter : unit -> ^awt)
                                        and ^awt :> ICriticalNotifyCompletion 
                                        and ^awt : (member get_IsCompleted : unit -> bool)
                                        and ^awt : (member GetResult : unit -> ^inp) >
        (abl : ^abl, continuation : ^inp -> 'out TaskStep) : 'out TaskStep =
            let awt = (^abl : (member GetAwaiter : unit -> ^awt)(abl))
            if (^awt : (member get_IsCompleted : unit -> bool)(awt)) 
            then continuation (^awt : (member GetResult : unit -> ^inp)(awt))
            else Continuation (awt, fun () -> continuation (^awt : (member GetResult : unit -> ^inp)(awt)))

module TaskStep =
    let inline bind f step : TaskStep<'a> =
        Binder<'a>.GenericAwait(step, f)

    let inline toTask (step: TaskStep<'a>) =
        try
            match step with
            | Value x -> Task.FromResult(x)
            | AsyncValue t -> t
            | Continuation _ as step -> StateMachine<'a>(step).Run().Unwrap()
        with
        | exn ->
            let src = new TaskCompletionSource<_>()
            src.SetException(exn)
            src.Task

module Task =
    let inline bind f task : Task<'a> =
        TaskStep.bind f task |> TaskStep.toTask

    let inline map f task : Task<'b> =
        bind (f >> Value) task

关于f# - 如何实现Task.Map,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59061122/

相关文章:

f# - 为什么 F# 中三引号字符串中的换行符与 Environment.NewLine 不同?

.net - F# 中泛型类型的运行时强制

architecture - 大型企业、功能性 F# 开发中通常使用什么架构模型?

f# - 如何编写通用 F# 委托(delegate)声明?

lambda - 将 F# func 转换为 Expression<Func<..,..>>

f# - 如何从 F# 中的任务编写 SelectMany

facebook - 如何使用 F# 获取 Facebook 访问 token

f# - C# async/await 方法到 F#?

selenium-webdriver - Canopy - 使用选择列表

f# - 为什么 F# 编译器会因为这个中缀运算符而失败?