5

我在标准库中找不到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 中一样。我是否遗漏了什么,或者是否有第三方库可以编写这类东西,或者我必须重新发明这个轮子?

4

2 回答 2

5

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

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

于 2018-07-25T15:50:45.083 回答
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 的答案将包含您需要的所有信息。

于 2018-07-25T21:21:45.897 回答