2012-05-14 38 views
12

我讀RWH,我已經來到第9章介紹了下面的一段代碼:「手柄」功能和真實世界哈斯克爾

import System.IO 
import Control.Exception 

saferFileSize :: FilePath -> IO (Maybe Integer) 
saferFileSize path = handle (\_ -> return Nothing) $ do 
    h <- openFile path ReadMode 
    size <- hFileSize h 
    hClose h 
    return (Just size) 

它不會然而編譯,從而以下錯誤信息:

test.hs:5:22: 
    Ambiguous type variable `e0' in the constraint: 
     (Exception e0) arising from a use of `handle' 
    Probable fix: add a type signature that fixes these type variable(s) 
    In the expression: handle (\ _ -> return Nothing) 
    In the expression: 
     handle (\ _ -> return Nothing) 
     $ do { h <- openFile path ReadMode; 
      size <- hFileSize h; 
      hClose h; 
      return (Just size) } 
    In an equation for `saferFileSize': 
     saferFileSize path 
      = handle (\ _ -> return Nothing) 
      $ do { h <- openFile path ReadMode; 
        size <- hFileSize h; 
        hClose h; 
        .... } 

這是怎麼回事?爲什麼不編譯?

回答

25

沒過多長時間RWH出來後,異常接口改爲支持更靈活的處理程序,其中類型處理程序確定它將捕獲哪些異常。例如。一個需要SomeException的處理程序將捕獲任何東西(通常不是一個好主意),而需要IOException的處理程序只會捕獲IO異常。

由於這個原因,很容易遇到類似於你的例子中的「無所事事」處理程序的模糊問題,因爲編譯器無法推斷出你試圖捕獲什麼類型的異常。解決這個問題的簡單方法是爲您的處理函數提供一個類型簽名。

handle ((\_ -> return Nothing) :: IOException -> IO (Maybe Integer)) $ do ... 

雖然這可能有些冗長。另一種解決方案是專門handle

handleIO :: (IOException -> IO a) -> IO a -> IO a 
handleIO = handle 

然後,你可以只使用handleIO,每當你想處理IO的異常,而不必拼出處理程序的類型簽名。

saferFileSize path = handleIO (\_ -> return Nothing) $ do ... 

第三種選擇是使用ScopedTypeVariables擴展,它(除其他事項外)允許您爲函數的只是參數提供類型註釋,使其餘的推斷。

{-# LANGUAGE ScopedTypeVariables #-} 
saferFileSize path = handle (\(_ :: IOException) -> return Nothing) $ do ... 
+0

Haskell網站上'handle'函數的文檔對此很不清楚(至少入門級別的人 - 需要文檔的人)https://wiki.haskell.org/Exception感謝編譯器只需要我們指定異常類型來處理的非常明確的解釋! – jocull

4

RWH相當古老。在GHC 6.10左右,handle函數簽名已經改變。

要使用舊版本,請導入Control.OldException而不是Control.Exception。您將得到棄用警告,但程序將編譯。

或者你可以使用新的接口,並給予處理程序明確的簽名,就像這樣:

((\ _ -> return Nothing) :: IOException -> IO (Maybe Integer))