Geo*_*off 16 monads f# asynchronous monad-transformers computation-expression
假设我想Option在async工作流程中返回一段时间:
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.
一种简单的方法是使用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.大多数情况下,我认为使用它们就足够了.
为了获得使用这些功能的感觉,请看一下这篇好文章.