结合F#async和计算表达式



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

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

理想情况下,我会在使用async的同时使用FSharpx中的maybe计算表达式,以避免执行match。我可以制作一个自定义生成器,但有没有一种方法可以通用地组合两个计算表达式?它可能看起来像这样:

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

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

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

#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中。

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

有关这种方法的更多信息,请阅读Monad Transformers。

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

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

我想您不必经常在async的上下文中处理option。FSharpx还为option类型提供了更多的高阶函数。在大多数情况下,我认为使用它们就足够了。

为了获得使用这些函数的感觉,请看一看这篇漂亮的文章。

type MaybeMonad() = 
    member __.Bind(x, f) = 
        match x with
        | Some v -> f v
        | None -> None
    member __.Return(x) = 
        Some x
let maybe = MaybeMonad()
let run = async {
    let! x = doAsyncThing
    let! y = doNextAsyncThing x
    return maybe {
        let! y_val = y
        return f y_val
    }
}

只需在里面使用f#计算表达式。

相关内容

  • 没有找到相关文章

最新更新