我在标准库中找不到对象choice
,这使我可以编写
let safeDiv (numer : Choice<Exception, int>) (denom : Choice<Exception, int>) =
choice {
let! n = numer
let! d = denom
return! if d = 0
then Choice1Of2 (new DivideByZeroException())
else Choice2Of2 (n / d)
}
就像在Haskell中一样。我是否错过了任何东西,或者是否有第三方库来编写此类内容,还是我必须重新发明这个轮子?
答案 0 :(得分:5)
Choice<'a,'b>
类型没有内置的计算表达式。通常,F#没有为常用的Monad提供内置的计算表达式,但是它确实提供了一种相当简单的方法来自己创建它们:Computation Builders。 This series是一个很好的教程,介绍了如何自己实现它们。 F#库通常确实定义了一个bind
函数,可以用作Computation Builder的基础,但对于Choice
类型却没有一个函数(我怀疑是因为有很多变化Choice
。
根据您提供的示例,我怀疑F#Result<'a, 'error>
类型实际上更适合您的情况。几个月前,有一个code-review用户在其中发布了Either
计算生成器,并且如果您想利用它,则可以接受较为完整的答案。
答案 1 :(得分:1)
值得注意的是,与Haskell不同,使用异常是处理F#中异常情况的一种完全可接受的方法。语言和运行时都对异常提供了一流的支持,使用它们也没有错。
我知道您的safeDiv
函数是用于说明,而不是一个实际问题,因此没有理由显示如何使用异常编写该函数。
在更现实的情况下:
如果仅当实际发生错误(网络故障等)时才发生异常,那么我只想让系统抛出异常并在需要重新启动时使用try ... with
进行处理工作或通知用户。
如果异常表示预期的结果(例如无效的用户输入),则如果您定义自定义数据类型来表示错误的状态(而不是使用Choice<'a, exn>
没有语义含义。)
还值得注意的是,仅当您需要将特殊行为(异常传播)与普通计算混合时,计算表达式才有用。我认为通常希望尽可能避免这种情况(因为它会使效果与纯计算交错)。
例如,如果要进行输入验证,则可以定义以下内容:
let result = validateAll [ condition1; condition2; condition3 ]
相对于计算表达式,我更愿意这样做:
let result = validate {
do! condition1
do! condition2
do! condition3 }
也就是说,如果您绝对确定需要用于错误传播的自定义计算生成器,那么Aaron的答案将提供您所需的所有信息。