[英]Functional Programming: How to handle exceptions in Functional Programming or what is its equivalent
假設,我有以下代碼。
public int divide(int dividend, int divisor) {
if( divisor == 0 || (dividend == Integer.MIN_VALUE && divisor == -1))
throw new DivisionException();
return dividend/divisor;
}
如何在函數式編程中寫這個?
我有一個類似於上述用 Java 編寫的邏輯,並希望將其遷移到 Haskell/Clojure 中的功能代碼。 如何在divide
的調用者中處理這個?
我知道上面的代碼是完全必要的。 它的編寫並沒有考慮將來將其遷移到 FP。
請用Haskell或Clojure 中的示例代碼為我提供建議。
下面展示了如何在 Haskell 中做到這一點。
根據上式siginure divide :: Int -> Int -> Either [Char] Int
你可以看到,功能divide
將返回一個Left string
或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"
你可以在repl.it上玩它
在 Haskell 中,您可以使用error "and your error message"
拋出錯誤,但這會使您的程序崩潰……這不是我們想要的。
divide
函數不是全數:其輸入域中的某些值沒有圖像。
更改輸出域,使其可以返回錯誤或數字。 調用者負責檢查該值是否真的是一個數字,還是一個錯誤。
在像 Clojure 這樣的動態類型語言中,您可以返回nil
,但任何其他值也可以工作,只要您能將它與數字區分開來。 在像 Haskell 這樣的靜態類型語言中,如果需要,可以使用Data.Either
或您自己的數據類型。
該檢查在 Haskell 中以一致且靜態的方式完成。 您必須每次都進行檢查,即使您確定除數不能為空。 但是,您也可以擁有一個包裝函數must-divide
,它會在出現錯誤時引發異常。
在 Clojure 中,您可能會忘記檢查nil
,這可能是由於錯誤或因為您比編譯器擁有更多關於除數的信息。 但是,您可以通過導出需要您考慮錯誤路徑的divide
宏來強制進行一致檢查:
(divide xy :on-error (throw ...)) (divide xy :on-error default-value)
...可以分別擴展為:
(or (maybe-divide xy) (throw ...)) (or (maybe-divide xy) default-value)
...與
(defn maybe-divide [dividend divisor] (and (not (zero? divisor)) (or (not= Integer/MIN_VALUE dividend) (not= -1 divisor)) (/ dividend divisor)))
數學運算被組合成更大的表達式:在其中添加顯式錯誤處理路徑可能很快變得不可讀。 此外,您可能希望您的大部分操作使用有效輸入調用divide
,並且不想在每次調用時檢查結果是否有效(例如,某些數學方程帶有證明除數不可能永遠為空)。 在這種情況下,Clojure 和 Haskell 支持異常。 這允許您在有錯誤的情況下在調用堆棧中捕獲更高的錯誤。
在 Clojure 中,這與 Java 並沒有什么不同:
(defn divide
[dividend divisor]
(if (or (zero? divisor)
(and (= Integer/MIN_VALUE
dividend)
(= -1 divisor)))
(throw (DivisionException.))
(/ dividend divisor)))
您的代碼不會改變任何變量,因此已經非常實用。 異常也是 Clojure 的一部分,因為它采用了 JVM 的執行模型。
在 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 ..)
將返回一個 Failure 對象,您可以將其用於錯誤處理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.