函数式编程:如何处理函数式编程中的异常或其等价物

Vic*_*cky 8 java haskell design-patterns functional-programming clojure

让我们说,我有以下代码.

public int divide(int dividend, int divisor) {
    if( divisor == 0 || (dividend == Integer.MIN_VALUE && divisor == -1)) 
          throw new DivisionException();
    return dividend/divisor;
}
Run Code Online (Sandbox Code Playgroud)

如何在函数式编程中编写?

我有一个类似于上面用Java编写的逻辑,并希望将其迁移到Haskell/Clojure中的功能代码.如何在呼叫者中处理此问题divide

我知道上面的代码是完全必要的.它不是在未来将其迁移到FP的预先编写的.

请在HaskellClojure中使用示例代码告诉我.

Rom*_*man 10

以下显示了如何在Haskell中完成它.

根据类型siginure,divide :: Int -> Int -> Either [Char] Int您可以看到该函数divide将返回a Left string或a Right Int.

Either是一个代数数据结构,还有更多,你可以自己编写.

divide :: Int -> Int -> Either [Char] Int
divide dividend divisor
    | (divisor == 0) = Left "Sorry, 0 is not allowed :o"
    | (dividend == (minBound :: Int)) && (divisor == -1) = Left "somethig went wrong"
    | otherwise = Right (dividend `div` divisor)


main = do
    print (divide 4 2)                     -- Right 2
    print (divide 4 0)                     -- Left "Sorry, 0 is not allowed :o"
    print (divide (minBound :: Int) (-1))  -- Left "somethig went wrong"
Run Code Online (Sandbox Code Playgroud)

你可以在repl.it上玩它

在Haskell中你可以抛出错误,error "and your error message"但这会让你崩溃..这不是我们想要的.


cor*_*ump 5

divide函数不是全部:其输入域中的某些值没有图像。

使函数总计

更改输出域,使其可以返回错误或数字。调用者负责检查该值是否真的是一个数字,还是一个错误。

在像 Clojure 这样的动态类型语言中,您可以 return nil,但任何其他值也可以工作,只要您能将它与数字区分开来。在像 Haskell 这样的静态类型语言中,Data.Either如果需要,请使用或您自己的数据类型。

  • 该检查在 Haskell 中以一致且静态的方式完成。您必须每次都进行检查,即使您确定除数不能为空。但是,您也可以拥有一个包装函数 ,must-divide然后它会在出现错误时引发异常。

  • 在 Clojure 中,您可能会忘记检查nil,可能是因为错误,或者是因为您比编译器拥有更多关于除数的信息。但是,您可以通过导出divide需要您考虑错误路径的宏来强制进行一致检查:

    (divide x y :on-error (throw ...))
    (divide x y :on-error default-value)
    
    Run Code Online (Sandbox Code Playgroud)

    ...可以分别扩展为:

    (or (maybe-divide x y) (throw ...))
    (or (maybe-divide x y) default-value)
    
    Run Code Online (Sandbox Code Playgroud)

    ... 和

    (defn maybe-divide [dividend divisor]
      (and (not (zero? divisor)) 
           (or (not= Integer/MIN_VALUE dividend)
               (not= -1 divisor))
           (/ dividend divisor)))
    
    Run Code Online (Sandbox Code Playgroud)

抛出异常

数学运算被组合成更大的表达式:在其中添加显式错误处理路径可能很快变得不可读。此外,您可能希望您的大部分操作都divide使用有效输入进行调用,并且不想在每次调用时检查结果是否有效(例如,某些数学方程带有证明除数永远不可能是空值)。在这种情况下,Clojure 和 Haskell 支持异常。这允许您在有错误的情况下在调用堆栈中捕获更高的错误。


Mic*_*ent 2

在 Clojure 中,这与 Java 没有什么不同:

(defn divide
  [dividend divisor]
  (if (or (zero? divisor)
          (and (= Integer/MIN_VALUE
                  dividend)
               (= -1 divisor)))
    (throw (DivisionException.))
    (/ dividend divisor)))
Run Code Online (Sandbox Code Playgroud)

您的代码不会改变任何变量,因此已经非常实用。异常也是 Clojure 的一部分,因为它采用了 JVM 的执行模型。

  • 请注意,这是一个“部分”函数的示例,该函数并未为所有输入定义,因为它会引发异常。(与 *total* 函数对比,如 [Roman 的 Haskell 示例](/sf/answers/3367876291/) 所示的函数,它为任何输入返回一些值。) (2认同)