[英]Why can Haskell exceptions only be caught inside the IO monad?
任何人都可以解釋為什么異常可能會被拋出IO monad,但可能只會被捕獲到它內部?
(純)Haskell函數的一個簡潔屬性是它們的單調性 - 更明確的參數產生更多定義的值。 這個屬性非常重要,例如推理遞歸函數(閱讀文章以了解原因)。
根據定義表示異常是底部, _|_
,poset中對應於給定類型的最小元素。 因此,為了滿足單調性要求,以下不等式需要適用於Haskell函數的任何表示f
:
f(_|_) <= f(X)
現在,如果我們可以捕獲異常,我們可以通過“識別”底部(捕獲異常)並返回更多定義的值來打破這種不等式:
f x = case catch (seq x True) (\exception -> False) of
True -> -- there was no exception
undefined
False -> -- there was an exception, return defined value
42
這是完整的工作演示(需要base-4 Control.Exception):
import Prelude hiding (catch)
import System.IO.Unsafe (unsafePerformIO)
import qualified Control.Exception as E
catch :: a -> (E.SomeException -> a) -> a
catch x h = unsafePerformIO $ E.catch (return $! x) (return . h)
f x = case catch (seq x True) (\exception -> False) of
True -> -- there was no exception
undefined
False -> -- there was an exception, return defined value
42
正如TomMD指出的那樣,另一個原因是打破了參考透明度。 你可以用相同的東西取代平等的東西,得到另一個答案 (在指稱意義上相等,即它們表示相同的值,而不是==
意義。)
我們怎么做? 請考慮以下表達式:
let x = x in x
這是一個非終止遞歸,因此它永遠不會返回任何信息,因此也由_|_
。 如果我們能夠捕獲異常,我們可以編寫函數f,如
f undefined = 0
f (let x = x in x) = _|_
(對於嚴格的函數,后者總是如此,因為Haskell沒有提供檢測非終止計算的方法 - 原則上不能,因為Halting問題 。)
因為異常會破壞參照透明度 。
您可能正在討論實際上直接輸入結果的異常。 例如:
head [] = error "oh no!" -- this type of exception
head (x:xs) = x
如果你感到遺憾的是無法捕獲這樣的錯誤,那么我告訴你,函數不應該依賴於error
或任何其他異常,而應該使用正確的返回類型( Maybe
, Either
,或MonadError )。 這迫使您以更明確的方式處理異常情況。
與上述不同(以及導致問題背后的問題),異常可以來自諸如內存條件完全獨立於計算值的信號。 這顯然不是一個純粹的概念,必須存在於IO中。
我的解釋可能有問題,但這就是我理解的方式。
由於函數在Haskell中是純粹的,因此編譯器有權按照他希望的任何順序對它們進行求值,並且它仍然會產生相同的結果。 例如,給定的功能:
square :: Int -> Int
square x = x * x
表達square (square 2)
可以用不同的方式進行評估,但它總是減少到相同的結果,即16。
如果我們從其他地方打電話給square
:
test x = if x == 2
then square x
else 0
當實際需要值時,可以在test
函數“外部”評估square x
。 在那一刻,調用堆棧可能與您在Java中所期望的完全不同。
因此,即使我們想捕捉square
拋出的潛在異常,您應該在哪里放置catch
部分?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.