无点函数中的异常处理

byt*_*ter 3 f# pointfree

我遇到了一个看似微不足道的问题:如果函数以无点方式编写,我无法处理函数中的异常.

考虑这两个功能:

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适当的方式处理异常(除了特别声明其参数),我该怎么办?

Tom*_*cek 7

这是我发现无点样式有问题的原因之一.这使得很难使用标准语言结构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#代码表示.