18

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

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

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

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

3 回答 3

22

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

或者,您可以使用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),第二个函数处理两者,所以它只需要被“打包”到我们的OptionTDU 中。

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

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

于 2015-10-05T16:24:11.870 回答
7

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

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

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

要获得使用这些功能的感觉,请查看这篇不错的文章

于 2012-09-11T19:55:32.173 回答
2
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# 计算表达式。

于 2017-03-29T09:21:34.360 回答