Ger*_*old 2 f# functional-programming scala option-type
F#和Scala都充当一种混合语言,通常用于将传统的面向对象代码的单词桥接到功能代码。
一个更属于OO世界的概念是个例外,而在许多情况下,功能世界更喜欢Option类型。为了包装依赖于异常的现有库代码并使之更具功能性,我因此想“提升”抛出异常的代码,以代替返回一个选项类型。
在Scala中,有一个不错的库函数可以“全部捕获”并转换为option。可以这样使用:
import scala.util.control.Exception._
val functionalVersion = allCatch opt myFunction
Run Code Online (Sandbox Code Playgroud)
请参见在Scala中,是否存在用于将异常转换为Options的预先存在的库函数?
现在,我要使用F#,但我有相同的要求,但是似乎无法为此找到一个现有的实用程序功能,而且自己也很难实现。
我可以为单元功能(也称为动作)创建这样的包装器
let catchAll f = try Some (f()) with | _ -> None
Run Code Online (Sandbox Code Playgroud)
但是这里的问题是我不想先将所有抛出代码的代码包装到一个动作中。
例如,我想包装数组索引运算符,以便它不会抛出。
// Wrap out-of-bounds exception with option type
let maybeGetIndex (array: int[]) (index: int) = catchAll (fun () -> array.[index])
maybeGetIndex [| 1; 2; 3 |] 10 // -> None
Run Code Online (Sandbox Code Playgroud)
但是,如果可以简单地编写,那就更好了。
(catchAll a.[index])
Run Code Online (Sandbox Code Playgroud)
即在计算之前将catchAll应用于整个表达式。(Scala可以通过F#似乎缺少的按名字调用参数来实现此目的)
所以这个问题是双重的:
首先,我认为“更多地属于面向对象的世界的概念是例外”是不正确的。ML系列的许多功能语言中都存在异常,例如,OCaml非常依赖于它们,甚至将它们用于某些控制流结构。在F#中,情况并非如此,因为.NET异常要慢一些,但是我看到异常非常与面向对象/功能问题正交。
因此,我实际上发现了F#中通常比选项类型更可取的异常。缺点是类型检查较少(您不知道可能会抛出什么),但缺点是该语言为异常提供了很好的集成语言支持。如果您需要处理特殊情况,那么异常是解决问题的好方法!
为了回答有关语法技巧的原始问题,我可能会像现有代码一样使用一个函数,因为这是显式且易于理解的(并且大概只需要在实现的某些核心函数中进行异常包装即可) )。
就是说,您可以定义一个计算表达式构建器,该构建器将代码包装在正文中,catchAll并以稍微更整洁的语法充当您的函数:
type CatchAllBuilder() =
member x.Delay(f) = try Some(f()) with _ -> None
member x.Return(v) = v
let catchAll = CatchAllBuilder()
Run Code Online (Sandbox Code Playgroud)
这使您可以编写如下内容:
catchAll { return Array.empty.[0] }
Run Code Online (Sandbox Code Playgroud)
如前所述,我不会这样做,因为(i)我认为不需要将所有异常都转换为F#中的选项,并且(ii)引入了陌生的语法,可能会使新团队成员(以及将来的您)感到困惑,但这可能是您可以获得的最好的语法。
[编辑:现在有一个工作版本return-这虽然不太漂亮,但也许仍然有用!]