最近在编写一些与许多嵌套异步工作流一起工作的代码时,我发现了一种对我来说很有味道的模式。一个简单的例子:
let flip f x y = f y x
let slowInc x = async {
do! Async.Sleep 500
printfn "Here you go, %d" x
}
let verboseFun inp = async {
match List.tryFind (flip (>) 3) inp with
| Some x -> do! slowInc x
| _ -> ()
}
verboseFun [1..5] |> Async.RunSynchronously
'verboseFun'对我来说似乎很啰嗦,但我想不出一种方法来结合Option和Async monads,这样它就可以在没有模式匹配的情况下重写。我在想
let terseFun inp = async {
inp
|> List.tryFind (flip (>) 3)
|> Option.iterAsync slowInc
}
在我看来,我很可能只是不知道有什么构建模块可以实现这一点。
编辑:托马斯回答后的额外澄清。
我试着调整那些对我来说微不足道的东西,如果一切都是同步的,例如,
let terseFun inp =
inp
|> List.tryFind (flip (>) 3)
|> Option.iter someSideEffectFunciton
成为嵌套异步工作流的一部分。本来我想的是"扔个蛋吧!"于是我就写了
let terseFun inp = async {
inp
|> List.tryFind (flip (>) 3)
|> Option.iter (fun x -> async { do! someSideEffectFunciton x })
|> ignore
}
但我立刻觉得不对劲,因为VS开始要求忽略。
ExtCore库有一堆帮助函数,可以让你使用返回可选值的异步计算,即类型为Async<'T option>
,它甚至定义了asyncMaybe
计算生成器来处理它们。
我没有广泛使用它,但是从我做的一些简单的实验来看,它看起来并没有很好地与f#的其他async
功能集成,但如果你想朝这个方向走,ExtCore可能是最好的库。
下面使用AsyncMaybe.Array
中的iter
函数(来源在这里)。这有点难看,因为我不得不让slowInc
的类型为Async<unit option>
,但它非常接近你所要求的:
let slowInc x = async {
do! Async.Sleep 500
printfn "Here you go, %d" x
return Some ()
}
let verboseFun inp =
inp
|> List.tryFind (fun x -> 3 > x)
|> Array.ofSeq
|> AsyncMaybe.Array.iter slowInc
|> Async.Ignore
除此之外,我还删除了flip
函数,因为这通常不是f#中推荐的样式(它倾向于使代码晦涩)。
也就是说,我认为你并不真的需要一个完整的ExtCore库。从你发布的一个例子中很难看出你的一般模式是什么,但是如果你所有的代码片段看起来都和你发布的相似,你可以定义你自己的asyncIter
函数,然后在其他地方使用它:
let asyncIter f inp = async {
match inp with
| None -> ()
| Some v -> do! f v }
let verboseFun inp =
inp
|> List.tryFind (fun x -> x > 3)
|> asyncIter slowInc
f#的伟大之处在于,你可以很容易地自己编写这些抽象,并使它们完全符合你的需求:-)