與evaluate
,force
和try
實驗,以下@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
的異常將不會被捕獲,例如ioA
是serialized <- 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建議使用readMay
或readMaybe
來重寫整個代碼,並且還直接強制嚴格讀取文件。
如果您試圖解決特定問題,您可能需要嘗試安裝* safe *軟件包。 [安全模塊](http://hackage.haskell.org/package/safe-0.3.3/docs/Safe.html)導出了其中的'readMay',它將讀取的值作爲'Maybe'值返回。 – kqr
@kqr謝謝,這是一個很好的建議。我將把它用於我的下一個「閱讀」。 – Perseids