Asynchronous workflows automatically support error handling through exceptions, so the idiomatic solution is to just use exceptions. If you want to distinguish some special kind of errors, then you can just define a custom exception type:
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 }
If you want to handle exceptions, you can use try ... with MyError msg -> ...
inside an asynchronous workflow.
You could define a custom computation builder that re-implements this using an algebraic data type such as your Result
, but unless you have some really good reason for doing that, I would not recommend this approach - it will not work with standard libraries, it is quite complicated and does not fit with the general F# style.
In your computation expression, the type of values is Async<Result<'T>>
, return
automatically wraps the argument of type 'T
in an async workflow that returns Ok
. If you wanted to construct a value representing a failure, you can use return!
and create an async workflow that returns Result.Error
. You probably need something like this:
let c = mymonad {
return! async.Return(Result.Error "Some message")
}
let d = mymonad {
return 1000
}
But as I said, using exceptions is a better approach.
EDIT: To answer the question in the comments - if you have a number of async computations, you can still wrap the final result in your custom type. However, you do not need to rebuild the entire asynchronous workflow library - errors in the primitive operations can still be handled using standard exceptions:
// 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 }