"Lifting" 选项类型的例外
"Lifting" exceptions to Option types
F# 和 Scala 都是一种混合语言,通常用于将传统的面向对象代码与函数式代码联系起来。
更多属于 OO 世界的概念是例外,而功能世界在许多情况下更喜欢 Option 类型。
为了包装依赖于异常的现有库代码 - 并使其更具功能性 - 因此我想 "lift" 异常抛出代码来代替 return 一个选项类型。
在 Scala 中,有一个很好的库函数可以 "catch all" 并转换为选项。可以这样使用:
import scala.util.control.Exception._
val functionalVersion = allCatch opt myFunction
见In Scala, is there a pre-existing library function for converting exceptions to Options?
既然我要转向 F#,我也有同样的要求,但我似乎无法为此找到一个现有的实用函数 - 而且我自己也很难实现一个。
我可以为一个单元函数创建这样一个包装器,也就是一个动作
let catchAll f = try Some (f()) with | _ -> None
但这里的问题是我不想首先将所有异常抛出代码包装到一个动作中。
例如,我想包装数组索引运算符,这样它就不会抛出异常。
// Wrap out-of-bounds exception with option type
let maybeGetIndex (array: int[]) (index: int) = catchAll (fun () -> array.[index])
maybeGetIndex [| 1; 2; 3 |] 10 // -> None
不过如果能简单写一下就更好了
(catchAll a.[index])
即在计算之前将 catchAll 应用于整个表达式。
(Scala 可以通过 call-by-name parameters 实现,而 F# 似乎没有)
所以这个问题是双重的:
- 是否有现有的库函数将异常包装到选项中
类型?
- 是否有一种语言功能可以让我实现
是吗?
首先,我认为"concept that belongs more to the object-oriented world are exceptions"是不正确的。异常存在于 ML 家族的许多函数式语言中,例如,OCaml 非常依赖它们,甚至将它们用于某些控制流结构。在 F# 中,情况并非如此,因为 .NET 异常有点慢,但我看到异常与 object-oriented/functional 问题非常正交。
出于这个原因,我实际上发现异常通常比 F# 中的选项类型更可取。缺点是类型检查较少(您不知道会抛出什么),但优点是该语言为异常提供了很好的集成语言支持。如果您需要处理 异常 情况,那么异常是一个很好的方法!
为了回答您关于语法技巧的原始问题 - 我可能会像您现有的代码那样使用一个函数,因为它是明确且易于理解的(并且大概您只需要在某些地方进行异常包装您实现的核心功能)。
就是说,您可以定义一个计算表达式构建器,将代码包装在主体中并作为您的 catchAll
函数,语法更简洁:
type CatchAllBuilder() =
member x.Delay(f) = try Some(f()) with _ -> None
member x.Return(v) = v
let catchAll = CatchAllBuilder()
这让你可以这样写:
catchAll { return Array.empty.[0] }
如前所述,我不会这样做,因为 (i) 我认为 F# 中不需要将所有异常都转换为选项,并且 (ii) 它引入了新团队成员(以及未来的您)不熟悉的语法) 可能会被混淆,但这可能是您可以获得的最好的语法。
[编辑:现在是 return
的工作版本 - 这有点不那么漂亮,但也许仍然有用!]
F# 和 Scala 都是一种混合语言,通常用于将传统的面向对象代码与函数式代码联系起来。
更多属于 OO 世界的概念是例外,而功能世界在许多情况下更喜欢 Option 类型。 为了包装依赖于异常的现有库代码 - 并使其更具功能性 - 因此我想 "lift" 异常抛出代码来代替 return 一个选项类型。
在 Scala 中,有一个很好的库函数可以 "catch all" 并转换为选项。可以这样使用:
import scala.util.control.Exception._
val functionalVersion = allCatch opt myFunction
见In Scala, is there a pre-existing library function for converting exceptions to Options?
既然我要转向 F#,我也有同样的要求,但我似乎无法为此找到一个现有的实用函数 - 而且我自己也很难实现一个。
我可以为一个单元函数创建这样一个包装器,也就是一个动作
let catchAll f = try Some (f()) with | _ -> None
但这里的问题是我不想首先将所有异常抛出代码包装到一个动作中。
例如,我想包装数组索引运算符,这样它就不会抛出异常。
// Wrap out-of-bounds exception with option type
let maybeGetIndex (array: int[]) (index: int) = catchAll (fun () -> array.[index])
maybeGetIndex [| 1; 2; 3 |] 10 // -> None
不过如果能简单写一下就更好了
(catchAll a.[index])
即在计算之前将 catchAll 应用于整个表达式。 (Scala 可以通过 call-by-name parameters 实现,而 F# 似乎没有)
所以这个问题是双重的:
- 是否有现有的库函数将异常包装到选项中 类型?
- 是否有一种语言功能可以让我实现 是吗?
首先,我认为"concept that belongs more to the object-oriented world are exceptions"是不正确的。异常存在于 ML 家族的许多函数式语言中,例如,OCaml 非常依赖它们,甚至将它们用于某些控制流结构。在 F# 中,情况并非如此,因为 .NET 异常有点慢,但我看到异常与 object-oriented/functional 问题非常正交。
出于这个原因,我实际上发现异常通常比 F# 中的选项类型更可取。缺点是类型检查较少(您不知道会抛出什么),但优点是该语言为异常提供了很好的集成语言支持。如果您需要处理 异常 情况,那么异常是一个很好的方法!
为了回答您关于语法技巧的原始问题 - 我可能会像您现有的代码那样使用一个函数,因为它是明确且易于理解的(并且大概您只需要在某些地方进行异常包装您实现的核心功能)。
就是说,您可以定义一个计算表达式构建器,将代码包装在主体中并作为您的 catchAll
函数,语法更简洁:
type CatchAllBuilder() =
member x.Delay(f) = try Some(f()) with _ -> None
member x.Return(v) = v
let catchAll = CatchAllBuilder()
这让你可以这样写:
catchAll { return Array.empty.[0] }
如前所述,我不会这样做,因为 (i) 我认为 F# 中不需要将所有异常都转换为选项,并且 (ii) 它引入了新团队成员(以及未来的您)不熟悉的语法) 可能会被混淆,但这可能是您可以获得的最好的语法。
[编辑:现在是 return
的工作版本 - 这有点不那么漂亮,但也许仍然有用!]