我遇到了一个看似微不足道的问题:如果函数以无点方式编写,我无法处理函数中的异常.
考虑这两个功能:
let divide1 x y =
try
x / y
with
| :? System.DivideByZeroException -> 42
let divide2 =
try
(/)
with
| :? System.DivideByZeroException -> fun _ _ -> 42
let x1 = divide1 5 0 // works fine
let x2 = divide2 5 0 // doesn't handle an exception
Run Code Online (Sandbox Code Playgroud)
虽然两个函数看似相同,但它们有不同的类型:
val divide1: int -> int -> int
val divide2: (int -> int -> int)
Run Code Online (Sandbox Code Playgroud)
显然,divide2甚至没有尝试处理异常.它只是返回一个运算符.
为了以divide2适当的方式处理异常(除了特别声明其参数),我该怎么办?
这是我发现无点样式有问题的原因之一.这使得很难使用标准语言结构try .. with(或标准循环和其他F#功能),您需要使用自定义组合器替换它们.在这种情况下,您可以定义tryWith2在异常处理程序中包装双参数函数的组合器:
let tryWith2 f h a b =
try f a b // Call the function with two arguments
with e ->
// Try running the error handler with the exception
match h e with
| Some g -> g a b // Error handler provided another function
| _ -> reraise() // Error was not handled - reraise
Run Code Online (Sandbox Code Playgroud)
那么你可以用这样的无点样式编写函数(错误处理仍然没有点,但我不想让这太傻了:-))
let divide2 =
tryWith2 (/) (function
| :? System.DivideByZeroException -> Some(fun _ _ -> 42)
| _ -> None)
let x1 = divide2 5 0 // returns 42
let x2 = divide2 5 1 // returns 5
Run Code Online (Sandbox Code Playgroud)
当然,即使在F#中,点自由风格也很有用.例如,在编写DSL时,它是组合声明性规范的好方法(因为基元使用更高级别的抽象来表达某些东西).在这里,你需要表达一些非常接近普通F#代码的东西,我相信,这最好用普通的F#代码表示.