Choice<'T1,'T2> 上的 Monadic 操作
Monadic operations on Choice<'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)
}
喜欢Haskell。我是不是遗漏了什么,或者有没有第三方库可以写这种东西,还是我必须重新发明这个轮子?
Choice<'a,'b>
类型没有内置计算表达式。通常,F# 没有针对常用 Monad 的内置计算表达式,但它确实提供了一种相当简单的方法来自行创建它们:Computation Builders。 This series 是一个关于如何自己实现它们的很好的教程。 F# 库通常确实定义了一个 bind
函数,可以用作计算生成器的基础,但它没有用于 Choice
类型的函数(我怀疑是因为有很多变体Choice
).
根据您提供的示例,我怀疑 F# Result<'a, 'error>
类型实际上更适合您的场景。几个月前有一个 code-review 用户发布了一个 Either
Computation Builder,如果你想利用它,接受的答案有一个相当完整的实现。
值得注意的是,与 Haskell 不同,在 F# 中使用异常是一种完全可以接受的处理异常情况的方法。语言和运行时都首先 class 支持异常,使用它们没有错。
我知道你的 safeDiv
函数是为了说明,而不是一个现实世界的问题,所以没有理由展示如何使用异常来编写它。
在更现实的场景中:
如果异常仅在实际出现问题(网络故障等)时发生,那么我会让系统抛出异常并在 try ... with
处使用 try ... with
处理该异常您需要重新启动工作或通知用户。
如果异常代表预期的内容(例如无效的用户输入),那么如果您定义自定义数据类型来表示错误状态(而不是使用 Choice<'a, exn>
没有语义意义)。
还值得注意的是,计算表达式仅在需要将特殊行为(异常传播)与普通计算混合时才有用。我认为尽可能避免这种情况通常是可取的(因为它将效果与纯计算交织在一起)。
例如,如果您要进行输入验证,您可以定义如下内容:
let result = validateAll [ condition1; condition2; condition3 ]
与计算表达式相比,我更喜欢它:
let result = validate {
do! condition1
do! condition2
do! condition3 }
也就是说,如果您绝对确定错误传播的自定义计算构建器是您所需要的,那么 Aaron 的回答包含您需要的所有信息。
我在标准库中找不到对象 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。我是不是遗漏了什么,或者有没有第三方库可以写这种东西,还是我必须重新发明这个轮子?
Choice<'a,'b>
类型没有内置计算表达式。通常,F# 没有针对常用 Monad 的内置计算表达式,但它确实提供了一种相当简单的方法来自行创建它们:Computation Builders。 This series 是一个关于如何自己实现它们的很好的教程。 F# 库通常确实定义了一个 bind
函数,可以用作计算生成器的基础,但它没有用于 Choice
类型的函数(我怀疑是因为有很多变体Choice
).
根据您提供的示例,我怀疑 F# Result<'a, 'error>
类型实际上更适合您的场景。几个月前有一个 code-review 用户发布了一个 Either
Computation Builder,如果你想利用它,接受的答案有一个相当完整的实现。
值得注意的是,与 Haskell 不同,在 F# 中使用异常是一种完全可以接受的处理异常情况的方法。语言和运行时都首先 class 支持异常,使用它们没有错。
我知道你的 safeDiv
函数是为了说明,而不是一个现实世界的问题,所以没有理由展示如何使用异常来编写它。
在更现实的场景中:
如果异常仅在实际出现问题(网络故障等)时发生,那么我会让系统抛出异常并在
try ... with
处使用try ... with
处理该异常您需要重新启动工作或通知用户。如果异常代表预期的内容(例如无效的用户输入),那么如果您定义自定义数据类型来表示错误状态(而不是使用
Choice<'a, exn>
没有语义意义)。
还值得注意的是,计算表达式仅在需要将特殊行为(异常传播)与普通计算混合时才有用。我认为尽可能避免这种情况通常是可取的(因为它将效果与纯计算交织在一起)。
例如,如果您要进行输入验证,您可以定义如下内容:
let result = validateAll [ condition1; condition2; condition3 ]
与计算表达式相比,我更喜欢它:
let result = validate {
do! condition1
do! condition2
do! condition3 }
也就是说,如果您绝对确定错误传播的自定义计算构建器是您所需要的,那么 Aaron 的回答包含您需要的所有信息。