[英]Functional Programming: How to handle exceptions in Functional Programming or what is its equivalent
Let's say, I've the below code.假设,我有以下代码。
public int divide(int dividend, int divisor) {
if( divisor == 0 || (dividend == Integer.MIN_VALUE && divisor == -1))
throw new DivisionException();
return dividend/divisor;
}
How to write this in Functional Programming?如何在函数式编程中写这个?
I have a logic similar to the above written in Java and would like to migrate that to functional code in Haskell/Clojure.我有一个类似于上述用 Java 编写的逻辑,并希望将其迁移到 Haskell/Clojure 中的功能代码。 How to handle this in the callers of
divide
?如何在
divide
的调用者中处理这个?
I know that the above code is totally imperative .我知道上面的代码是完全必要的。 It was not written with the forethought of migrating it to FP in future.
它的编写并没有考虑将来将其迁移到 FP。
Kindly advise me with a sample code in Haskell or Clojure .请用Haskell或Clojure 中的示例代码为我提供建议。
The following shows how you could do it in Haskell.下面展示了如何在 Haskell 中做到这一点。
Based on the type siginure divide :: Int -> Int -> Either [Char] Int
you can see that the function divide
will return either a Left string
or a Right Int
.根据上式siginure
divide :: Int -> Int -> Either [Char] Int
你可以看到,功能divide
将返回一个Left string
或Right Int
。
Either
is an algebraic data structure and there are many more and you can write your own to. 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"
You can play with it on repl.it你可以在repl.it上玩它
In Haskell you can throw errors to with error "and your error message"
but this will crash you programm.. and this is not what we want.在 Haskell 中,您可以使用
error "and your error message"
抛出错误,但这会使您的程序崩溃……这不是我们想要的。
The divide
function is not total: some values from its input domain have no image. divide
函数不是全数:其输入域中的某些值没有图像。
Change the output domain so that it can return either an error or a number.更改输出域,使其可以返回错误或数字。 The caller is responsible for checking whether the value is really a number, or an error.
调用者负责检查该值是否真的是一个数字,还是一个错误。
In a dynamically typed language like Clojure, you could return nil
, but any other value could work too, as long as you can distinguish it from a number.在像 Clojure 这样的动态类型语言中,您可以返回
nil
,但任何其他值也可以工作,只要您能将它与数字区分开来。 In a statically typed language like Haskell, use Data.Either
or your own datatype if you need.在像 Haskell 这样的静态类型语言中,如果需要,可以使用
Data.Either
或您自己的数据类型。
The check is done consistently and statically in Haskell.该检查在 Haskell 中以一致且静态的方式完成。 You must do the check every time, even if you are sure the divisor can't be null.
您必须每次都进行检查,即使您确定除数不能为空。 However, you can also have a wrapper function,
must-divide
, which then would throws an exception on errors.但是,您也可以拥有一个包装函数
must-divide
,它会在出现错误时引发异常。
In Clojure, you may forget to check for nil
, either from a bug or because you have more information about the divisor than the compiler.在 Clojure 中,您可能会忘记检查
nil
,这可能是由于错误或因为您比编译器拥有更多关于除数的信息。 You could however force a consistent checking by exporting a divide
macro that requires you to consider the error path:但是,您可以通过导出需要您考虑错误路径的
divide
宏来强制进行一致检查:
(divide xy :on-error (throw ...)) (divide xy :on-error default-value)
... could be respectively expanded as: ...可以分别扩展为:
(or (maybe-divide xy) (throw ...)) (or (maybe-divide xy) default-value)
... with ...与
(defn maybe-divide [dividend divisor] (and (not (zero? divisor)) (or (not= Integer/MIN_VALUE dividend) (not= -1 divisor)) (/ dividend divisor)))
Mathematical operations are composed to form bigger expressions: adding an explicit error handling path inside them can quickly become unreadable.数学运算被组合成更大的表达式:在其中添加显式错误处理路径可能很快变得不可读。 Also, you may expect most of your operations to call
divide
with valid inputs, and don't want to check if the result is valid each time you call it (eg some mathematical equation come with a proof that the divisor won't possibly ever be null).此外,您可能希望您的大部分操作使用有效输入调用
divide
,并且不想在每次调用时检查结果是否有效(例如,某些数学方程带有证明除数不可能永远为空)。 In that case, Clojure and Haskell support exceptions.在这种情况下,Clojure 和 Haskell 支持异常。 This allows you to catch errors higher up in the call stack in case you have bugs.
这允许您在有错误的情况下在调用堆栈中捕获更高的错误。
In Clojure this isn't really different from Java:在 Clojure 中,这与 Java 并没有什么不同:
(defn divide
[dividend divisor]
(if (or (zero? divisor)
(and (= Integer/MIN_VALUE
dividend)
(= -1 divisor)))
(throw (DivisionException.))
(/ dividend divisor)))
Your code isn't mutating any variables and therefore is already pretty much functional.您的代码不会改变任何变量,因此已经非常实用。 Exceptions are just as much a part of Clojure, because it adopts the execution model of the JVM.
异常也是 Clojure 的一部分,因为它采用了 JVM 的执行模型。
In Clojure(script), you can use the Failjure library which provides a way to handle exceptions in a functionally pure way.在 Clojure(script) 中,您可以使用Failjure库,它提供了一种以纯函数方式处理异常的方法。
(ns stackoverflow
(:require
[failjure.core :as f]))
(defn divide
[dividend divisor]
(cond
(zero? divisor) (f/fail "The divisor is 0; unable to perform operation.")
(and (= Integer/MIN_VALUE dividend) (neg? divisor)) (f/fail "Unable to perform division with a negative dividend and divisor")
:else (/ dividend divisor)))
(f/fail ..)
will return a Failure object which you can use for your error handling. (f/fail ..)
将返回一个 Failure 对象,您可以将其用于错误处理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.