2013-11-04 112 views
5

我試圖從文件中讀取一些值並捕獲可能發生的每個異常(以"Easier to ask for forgiveness than permission"的心態)。儘管如此,我在遇到Prelude.read: no parse異常時遇到了問題。告訴try,它應該抓住每一個例外,我定義tryAny用顯式類型SomeException這在我的知識是每一個異常的「超類型」:無法用Control.Exception.try捕捉「Prelude.read:no parse」異常

import Control.Exception (try,SomeException) 
tryAny :: IO a -> IO (Either SomeException a) 
tryAny = try 

隨着tryAny我似乎能夠趕上IO錯誤:

> tryAny (fromFile "nonExistingFileName") 
Left nonExistingFileName: openFile: does not exist (No such file or directory) 

但讀取錯誤不會被捕獲:

> tryAny (return ((read "a")::Int)) 
Right *** Exception: Prelude.read: no parse 

我可以ð o抓住每一個異常?

+2

如果您試圖解決特定問題,您可能需要嘗試安裝* safe *軟件包。 [安全模塊](http://hackage.haskell.org/package/safe-0.3.3/docs/Safe.html)導出了其中的'readMay',它將讀取的值作爲'Maybe'值返回。 – kqr

+0

@kqr謝謝,這是一個很好的建議。我將把它用於我的下一個「閱讀」。 – Perseids

回答

4

return不評估它的參數,因此不會拋出任何異常。當您嘗試打印結果時,評估發生在tryAny之外。

使用evaluate(可能與force一起從Control.DeepSeq,這取決於您的實際情況)。

+2

而不是「評估」,使用明確建模失敗的解析庫可能會更簡單。即除'read'以外的任何其他內容。 –

+2

最近版本的'base'中有'readMaybe'。但是這個問題似乎是關於異常,而不是解析。 –

+0

的確如此,這就是爲什麼我高舉你的答案。但是'read'可能不適合這項工作。 –

2

evaluateforcetry實驗,以下@Roman Cheplyaka的建議的很多後,我想出了這個解決方案,這似乎是超炫明顯,現在我看到的最後結果:

import Control.DeepSeq (force,NFData) 
import Control.Exception (try,SomeException) 

tryAnyStrict :: NFData a => IO a -> IO (Either SomeException a) 
tryAnyStrict = try.evaluateIoCompletely 

evaluateIoCompletely :: NFData a => IO a -> IO a 
evaluateIoCompletely ioA = do 
    a <- ioA 
    evaluate $ force a 

該解決方案將該過程分解爲一個函數,該函數將執行嚴格的評估並拋出所有異常和一個將捕獲這些異常的包裝器。 force返回一個純粹的haskell對象,該對象本身必須先進行評估以強制對a進行深度評估。從documentation

force x = x `deepseq` x 

力X充分評估x,然後返回。請注意,力x僅在需要力x本身的值時執行評估,所以本質上它將淺層評估轉變爲深層評估。

有此實現,出了問題我在前面嘗試的幾個細微的問題:如果你使用ioA直接輸入到force而不是a,那麼你將最終的目標

  • 類型IO (IO a),因爲evaluate的類型爲b -> IO b。這個直接的問題可以通過IO(IO a) -> IO a類型的一些解包函數來解決,但是您會遇到類型錯誤假設NFData a,現在需要爲NFData (IO a)。但對於IO a,似乎沒有實例聲明可用,至少對於a::(String,String)
  • 您可以使用tryAnyStrict = do a <- ioA; (try.evaluate.force) a而不是使用兩個函數。然後問題是a <- ioA的異常將不會被捕獲,例如ioAserialized <- readFile; return $ read serialized的結果,並且要讀取的文件不存在。
  • 您不能在代碼中嵌入try.evaluateIoCompletely而沒有類型信息,因爲try需要知道它應該捕獲哪個異常。此信息現在通過返回類型tryAnyStrict提供,該類型將類型的try :: Exception e => IO a -> IO (Either e a)解析爲最常見的異常類型SomeException

現在,經過我tryAnyStrict工作,我仍然有懶評價的問題,因爲如果a <- ioA失敗 - 在IOA是read從一個文件中懶洋洋地讀一個字符串的結果 - (例如文件內容沒有read)所需的格式),那麼文件仍然沒有被完全讀取,因此仍然打開。隨後寫入文件將失敗(「openFile:resource busy」)。所以......我可能實際上用@kqr和@Roman Cheplyaka建議使用readMayreadMaybe來重寫整個代碼,並且還直接強制嚴格讀取文件。

1

我有同樣的問題,並沒有找到readMaybe建議在一些評論。但我發現它解決了我的問題的另一種有用的閱讀變化 - readIO

的readIO功能類似,只是,而不是終止程序的單子,這標誌着解析失敗的IO讀取。

使用它我能夠捕捉並處理程序中的所有錯誤。

-1

如果你可以使用safe-exceptions

您可以和使用tryAnyDeep

tryDeep :: (C.MonadCatch m, MonadIO m, E.Exception e, NFData a) => m a -> m (Either e a) 
tryDeep f = catch (liftM Right (evaluateDeep f)) (return . Left) 
tryAnyDeep :: (C.MonadCatch m, MonadIO m, NFData a) => m a -> m (Either SomeException a) 
tryAnyDeep = tryDeep 

但它解決方案中得到一個具有deriving (NFData)

+0

雖然這個鏈接可能回答這個問題,但最好在這裏包含答案的基本部分,並提供參考鏈接。如果鏈接頁面更改,則僅鏈接答案可能會失效。 - [來自評論](/ review/low-quality-posts/17302818) – EJoshuaS

+0

我追加說明。 – ncaq