结合F#异步和计算表达式

Geo*_*off 16 monads f# asynchronous monad-transformers computation-expression

假设我想Optionasync工作流程中返回一段时间:

let run = 
    async {
        let! x = doAsyncThing
        let! y = doNextAsyncThing x
        match y with
        | None -> return None
        | Some z -> return Some <| f z
    }
Run Code Online (Sandbox Code Playgroud)

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

let run = 
    async {
        let! x = doAsyncThing
        let! y = doNextAsyncThing x
        return! f y
    }
Run Code Online (Sandbox Code Playgroud)

Gus*_*Gus 21

通常在F#而不是使用通用工作流程中,您可以手动定义工作流程,或者使用在您的情况下可以使用的工作流程async,maybe但如果要将它们组合使用,则需要手动编写特定的工作流程组合.

或者你可以使用F#+这是一个为monad提供通用工作流的项目,在这种情况下它将自动为你导出,这是一个工作示例,使用你的工作流然后使用OptionT哪个是monad变换器:

#nowarn "3186"
#r @"FSharpPlus.dll"

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
Run Code Online (Sandbox Code Playgroud)

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

正如您所看到的,两个工作流程都是自动导出的,您拥有的工作流程(异步工作流程)和您想要的工作流程.

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


pad*_*pad 6

一种简单的方法是使用Option模块:

let run = 
    async {
        let! x = doAsyncThing
        let! y = doNextAsyncThing x
        return Option.map f y
    }
Run Code Online (Sandbox Code Playgroud)

我想你不必应付option在上下文async经常.FSharpx还为类型提供了更多高阶函数option.大多数情况下,我认为使用它们就足够了.

为了获得使用这些功能的感觉,请看一下这篇好文章.