With F#+ https://github.com/fsprojects/FSharpPlus您可以通过使用来组合应用程序Compose
:
#r @"nuget: FSharpPlus"
open FSharpPlus
open FSharpPlus.Data
// Generic applicative CE. At the moment not included in the library.
type ApplicativeBuilder<'a>() =
inherit MonadFxStrictBuilder<'a>()
member inline _.BindReturn(x, f) = map f x
let applicative<'t> = ApplicativeBuilder<'t> ()
let allGoodAsync () : Async<Validation<string, int>> = async { printfn "first success"; return Success 1 }
let thisResturnsErrorAsync () : Async<Validation<string, int>> = async { return Failure "thisReturnsError" }
let neverExecutedAsync () : Async<Validation<string, int>> = async { printfn "actually executed"; return Success 3 }
let x: Async<Validation<string, _>> =
applicative {
let! (a: int) = allGoodAsync () |> Compose
and! (b: int) = thisResturnsErrorAsync () |> Compose
and! (c: int) = neverExecutedAsync () |> Compose
return a + b + c
}
|> Compose.run
let y = Async.RunSynchronously x
结果是
>
first success
actually executed
val y: Validation<string,int> = Failure "thisReturnsError"
UPDATE
重新审视这个答案,可以进一步简化如下:
type ApplicativeBuilder2<'a>() =
inherit Builder<'a>()
member inline _.BindReturn(x, f) = map f x
member inline _.Source x = Compose x
member inline _.Run x = Compose.run x
let applicative2<'t> = ApplicativeBuilder2<'t> ()
所以你不需要搞乱Compose
:
let x: Async<Validation<string, _>> =
applicative2 {
let! (a: int) = allGoodAsync ()
and! (b: int) = thisResturnsErrorAsync ()
and! (c: int) = neverExecutedAsync ()
return a + b + c
}
由于评论中提到的 F# 类型推断错误仍未修复,我现在可以说applicative
and applicative2
肯定会包含在 F#+ 的下一版本中。