在async
工作流程中似乎有两种返回错误的方法:raise
和Result
。
let willFailRaise = async {
return raise <| new Exception("oh no!")
}
let willFailResult = async {
return Result.Error "oh no!"
}
对于呼叫者,处理方式有所不同:
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)
}
我的问题是:
答案 0 :(得分:6)
这是F#编程的许多方面之一,在语言及其社区的核心中受到思维分裂的困扰。
一方面,您拥有“ F#.NET Framework语言”,其中异常是用于处理错误的机制,另一方面,您可以从世界的Haskell借用其惯用法“ F#函数式编程语言”。这就是Result
(也称为Either
)的来源。
“哪个是惯用的”这个问题的答案将根据您询问的人和他们所看到的内容而改变,但是我的经验告诉我,you're better off using exceptions如有疑问。 Result
类型在适度中有其用途,但是结果密集型编程风格很容易失控,一旦发生这种情况就不太美观了。
答案 1 :(得分:5)
提高
优势
缺点:
结果
优势
缺点
bind
,map
和mapError
bimap : ('TSuccess -> 'a) -> ('TError -> 'e) -> Result<'TSuccess,'TError> -> Result<'a, 'e>
fold : ('TSuccess -> 'T) -> ('TError -> 'T) -> Result<'TSuccess, 'TError> -> 'T
答案 2 :(得分:4)
这取决于我们在谈论哪种错误。基本上有三种:
NullReferenceException
或StackOverflowException
等)。尽管两种方法都可以完成工作,但是通常您需要考虑的是使您的代码具有自记录性并易于阅读。这意味着:
Result
。这些“错误”是预期的,它们是您工作流程的一部分。使用Result
可以在功能签名中反映您的业务规则,这非常有用。Result
可能会更方便。如果不是,请寻求例外。Exception
。首先,您无法用Result
覆盖所有内容,无论哪种方式都需要全局异常过滤器。第二件事-如果您试图掩盖所有可能的紧急情况-代码很快就会变成令人讨厌的灾难,这将使使用Result
来处理域错误的整个过程都丧命。因此,实际上这与Async
或C#互操作无关,它与代码的可读性和可维护性有关。
对于C#iterop -不用担心,Result
具有所有帮助方法,例如IsError
等。但您始终可以添加扩展方法:
[<AutoOpen>]
module Utils =
type Result<'Ok, 'Error> with
member this.Value =
match this with
| Ok v -> v
| Error e -> Exception(e.ToString()) |> raise