It seems like there are two ways to return errors in an async
workflow: raise
and Result
.
let willFailRaise = async {
return raise <| new Exception("oh no!")
}
let willFailResult = async {
return Result.Error "oh no!"
}
For the caller, the handling is a bit different:
async {
try
let! x = willFailRaise
// ...
with error ->
System.Console.WriteLine(error)
}
async {
let! maybeX = willFailResult
match maybeX with
| Result.Ok x ->
// ...
| Result.Error error ->
System.Console.WriteLine(error)
}
My questions are:
It depends on what kind of error we are talking about. Basically there are three kinds:
NullReferenceException
or StackOverflowException
etc.), which are caused by programmers' mistakes.While both approaches can get the job done, usually your concern should be to make your code as self-documented and easy-to-read as possible. Which means the following:
Result
. Those "errors" are expected, they are part of your workflow. Using Result
reflects your business rules in function's signature, which is very useful.Result
. If not -- go for exceptions.Exception
. First of all, you can't cover everything with Result
, you gonna need global exception filter either way. Second thing -- if you try to cover all possible panics - code becomes a nasty disaster extremely fast, that will kill the whole point of using Result
for domain errors.So really this has nothing to do with Async
or C# interop, it's about code readability and maintainability.
As for C# iterop -- don't worry, Result
has all the methods to help, like IsError
and so on. But you can always add an extension method:
[<AutoOpen>]
module Utils =
type Result<'Ok, 'Error> with
member this.Value =
match this with
| Ok v -> v
| Error e -> Exception(e.ToString()) |> raise
This is one of the many aspects of F# programming that suffers from the mind-split at the core of the language and its community.
On one hand you have "F# the .NET Framework language" where exceptions are the mechanism for handling errors, on the other - "F# the functional programming language" that borrows its idioms from the Haskell side of the world. This is where Result
(also known as Either
) comes from.
The answer to the question "which one is idiomatic" will change depending who you ask and what they have seen, but my experience has taught me that when in doubt, you're better off using exceptions. Result
type has its uses in moderation, but result-heavy programming style easily gets out of hand, and once that happens it's not a pretty sight.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With