[英]F# custom computation workflow
给出结果类型
type Result<'t> = OK of 't | Error of string
我有这些函数都返回Async <Result <'t>>这些函数组合如下:
let a = async { return Result.OK 1000 }
let b = async { return Result.Error "some message" }
let sum x y =
async {
let! r1 = x
match r1 with
| Result.OK v1 ->
let! r2 = y
match r2 with
| Result.OK v2 -> return v1 + v2
| Result.Error msg -> return Result.Error msg
| Result.Error msg -> return Result.Error msg
}
这段代码看起来很糟糕,所以我希望这样:
type Result = Ok of int | Error of string
type MyMonadBuilder() =
member x.Bind (v,f) =
async {
let! r = v
match r with
| Ok r' -> return! f r'
| Error msg -> return Error msg
}
member x.Return v = async {return Ok v }
member x.Delay(f) = f()
let mymonad = MyMonadBuilder()
let runMyMonad = Async.RunSynchronously
let a = mymonad { return 10 }
let b = mymonad { return 20 }
let c =
mymonad {
return Result.Error "Some message"
//??? The above doesn't work but how do I return a failure here?
}
let d =
async {
return Ok 1000
}
//how to wrap this async with mymonad such that I can use it together with my other computation expressions?
let sum x y =
mymonad {
let! v1 = x
let! v2 = y
return v1 + v2
}
[<EntryPoint>]
let main argv =
let v = sum a b |> runMyMonad
match v with
| Ok v' -> printfn "Ok: %A" v'
| Error msg -> printf "Error: %s" msg
System.Console.Read() |> ignore
0
所以问题是:
......这样我就可以写了
let f (a:MyMonad<int>) (b:MyMonad<string>) = ...
更新:
另外,我想并行运行几个mymonad操作,然后查看结果数组以查看错误和成功之处。 出于这个原因,我认为使用例外不是一个好主意。
另外,关于问题3,我的意思是使我的类型参数化并且不透明,以使调用者不知道/不在乎他们正在处理异步。 我编写monad的方式是调用方始终可以使用Async.RunSynchronously运行mymonad表达式。
更新2:
到目前为止,我最终得到以下结果:
代码如下所示:
type MyMonad<'t> = 't Result Async
type MyMonadBuilder() =
member x.Bind<'t> (v,f) : MyMonad<'t>=
async {
let! r = v
match r with
| Ok r' -> return! f r'
| Error msg -> return Error msg
}
member x.Return<'t> v : MyMonad<'t> = async {return Ok v }
member x.ReturnFrom<'t> v : MyMonad<'t> = v
member x.Delay(f) = f()
let failwith<'t> : string -> MyMonad<'t> = Result.Error >> async.Return
对于我的目的,这看起来相当不错。 谢谢!
异步工作流通过异常自动支持错误处理,因此惯用解决方案只是使用异常。 如果要区分某些特殊类型的错误,则可以只定义自定义异常类型:
exception MyError of string
// Workflow succeeds and returns 1000
let a = async { return 1000 }
// Workflow throws 'MyError' exception
// (using return! means that it can be treated as a workflow returning int)
let b = async { return! raise (MyError "some message") }
// Exceptions are automatically propagated
let sum = async {
let! r1 = a
let! r2 = b
return r1 + r2 }
如果要处理异常,可以在异步工作流中使用try ... with MyError msg -> ...
您可以定义一个自定义计算生成器,以使用诸如Result
类的代数数据类型重新实现此功能,但是除非您有充分的理由这样做,否则我不建议您使用这种方法-它不适用于标准库,很复杂,不适合一般的F#风格。
在您的计算表达式中,值的类型为Async<Result<'T>>
, return
自动将类型'T
的参数包装在返回Ok
的异步工作流中。 如果要构造表示失败的值,可以使用return!
并创建一个返回Result.Error
的异步工作流。 你可能需要这样的东西:
let c = mymonad {
return! async.Return(Result.Error "Some message")
}
let d = mymonad {
return 1000
}
但正如我所说,使用例外是一种更好的方法。
编辑:要在注释中回答问题-如果您有许多异步计算,您仍然可以将最终结果包装在您的自定义类型中。 但是,您不需要重建整个异步工作流库 - 仍可以使用标准异常处理基元操作中的错误:
// Primitive async work that may throw an exception
let primitiveAsyncWork = async { ... }
// A wrapped computation that returns standard Option type
let safeWork = async {
try
let! res = primitiveAsyncWork
return Some res
with e -> return None }
// Run 10 instances of safeWork in parallel and filter out failed computations
async { let! results = [ for i in 0 .. 9 -> safeWork ] |> Async.Parallel
return results |> Seq.choose id }
我的ExtCore库中的asyncChoice
工作流已经实现了-在NuGet上可用,因此您要做的就是添加对项目的引用,在源文件中打开ExtCore.Control
命名空间,并开始编写如下代码:
open ExtCore.Control
let asyncDivide100By (x : int) =
asyncChoice {
if x = 0 then
return! AsyncChoice.error "Cannot divide by zero."
else
return (100 / x)
}
let divide100By (x : int) =
let result =
asyncDivide100By x
|> Async.RunSynchronously
match result with
| Choice1Of2 result ->
printfn "100 / %i = %i" x result
| Choice2Of2 errorMsg ->
printfn "An error occurred: %s" errorMsg
[<EntryPoint>]
let main argv =
divide100By 10
divide100By 1
divide100By 0
0 // Exit code
asyncChoice是使用F#Core库中的标准Async<'T>
和Choice<_,_>
类型构造的,因此您不会出现任何兼容性问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.