结合 F# 异步和计算表达式 [英] Combine F# async and maybe computation expression

查看:26
本文介绍了结合 F# 异步和计算表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想在 async 工作流中返回一个 Option:

Say i want to return an Option while in an async workflow:

let run = 
    async {
        let! x = doAsyncThing
        let! y = doNextAsyncThing x
        match y with
        | None -> return None
        | Some z -> return Some <| f z
    }

理想情况下,我会在异步的同时使用来自 FSharpx 的可能计算表达式,以避免执行 match.我可以制作一个自定义构建器,但是有没有办法将两个计算表达式一般地组合起来?它可能看起来像这样:

Ideally I would use the maybe computation expression from FSharpx at the same time as async to avoid doing the match. I could make a custom builder, but is there a way to generically combine two computation expressions? It might look something like this:

let run = 
    async {
        let! x = doAsyncThing
        let! y = doNextAsyncThing x
        return! f y
    }

推荐答案

通常在 F# 中,您手动定义工作流而不是使用通用工作流,或者使用现成可用的工作流async 可能 但如果您想将它们结合使用,则需要手动编写特定的工作流组合.

Typically in F# instead of using generic workflows you define the workflow by hand, or use one that is ready available as in your case async and maybe but if you want to use them combined you will need to code a specific workflow combination by hand.

或者,您可以使用 F#+,这是一个为以下项目提供通用工作流程的项目monads,在这种情况下,它将自动为您派生,这是一个工作示例,使用您的工作流程,然后使用 OptionT 这是一个 monad 转换器:

Alternatively you can use F#+ which is a project that provides generic workflows for monads, in that case it will be automatically derived for you, here's a working example, using your workflow and then using OptionT which is a monad transformer:

#r "nuget: FSharpPlus, 1.2"

open FSharpPlus
open FSharpPlus.Data

let doAsyncThing = async {return System.DateTime.Now}
let doNextAsyncThing (x:System.DateTime) = async {
    let m = x.Millisecond  
    return (if m < 500 then Some m else None)}
let f x = 2 * x

// then you can use Async<_> (same as your code)
let run = monad {
    let! x = doAsyncThing
    let! y = doNextAsyncThing x
    match y with
    | None   -> return None
    | Some z -> return Some <| f z}

let res = Async.RunSynchronously run

// or you can use OptionT<Async<_>> (monad transformer)

let run' = monad {
    let! x = lift doAsyncThing
    let! y = OptionT (doNextAsyncThing x)
    return f y}

let res' = run' |> OptionT.run |> Async.RunSynchronously

第一个函数必须被提升"到另一个 monad 中,因为它只处理 Async(而不是 Option),第二个函数处理两者它只需要打包"到我们的 OptionT DU 中.

The first function has to be 'lifted' into the other monad, because it only deals with Async (not with Option), the second function deals with both so it only needs to be 'packed' into our OptionT DU.

正如您所看到的,两个工作流都是自动派生的,一个是您拥有的(异步工作流),另一个是您想要的.

As you can see both workflows are derived automatically, the one you had (the async workflow) and the one you want.

有关此方法的更多信息,请阅读Monad Transformers.

For more information about this approach read about Monad Transformers.

这篇关于结合 F# 异步和计算表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆