选择<'T1,'T2>上的一元运算



我在标准库中找不到允许我编写的对象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)
}

就像哈斯克尔一样。我错过了什么吗,或者有没有第三方库可以写这种东西,或者我必须重新发明这个轮子?

Choice<'a,'b>类型没有内置计算表达式。一般来说,F#没有为常用的Monad内置计算表达式,但它确实提供了一种自己创建它们的相当简单的方法:computation Builders。本系列是一个很好的教程,介绍如何自己实现它们。F#库通常定义了一个bind函数,该函数可以用作Computation Builder的基础,但它没有用于Choice类型的函数(我怀疑是因为Choice有很多变体(。

根据您提供的示例,我怀疑F#Result<'a, 'error>类型实际上更适合您的场景。几个月前有一篇代码评论,用户发布了一个Either计算生成器,如果你想利用它,接受的答案有一个相当完整的实现。

值得注意的是,与Haskell不同,使用异常是F#中处理异常情况的一种完全可以接受的方式。该语言和运行时都对异常有一流的支持,使用它们没有错。

我知道你的safeDiv函数只是为了说明,而不是一个现实世界中的问题,所以没有理由展示如何使用异常来编写它。

在更现实的场景中:

  • 如果只有在出现实际问题(网络故障等(时才会发生异常,那么我只会让系统抛出异常,并在需要重新启动工作或通知用户时使用try ... with进行处理。

  • 如果异常表示预期的内容(例如,无效的用户输入(,那么如果您定义一个自定义数据类型来表示错误的状态(而不是使用没有语义的Choice<'a, exn>(,您可能会得到更可读的代码。

还值得注意的是,只有当您需要将特殊行为(异常传播(与普通计算混合时,计算表达式才有用。我认为尽可能避免这种情况通常是可取的(因为它将效果与纯计算交织在一起(。

例如,如果您正在进行输入验证,您可以定义以下内容:

let result = validateAll [ condition1; condition2; condition3 ]

比起计算表达式,我更喜欢这个:

let result = validate {
do! condition1
do! condition2
do! condition3 }

也就是说,如果你绝对确定用于错误传播的自定义计算构建器是你所需要的,那么Aaron的答案就具备了你所需的所有信息。

最新更新