2012-05-14 11 views
12

私はRWHを読んでいる、と私は第9章これは、次のコードを紹介しに来ている:「ハンドル」機能と実世界Haskellの

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

、例外インタフェースをより柔軟ハンドラをサポートするように変更されましたどの例外が捕捉されるかが決定される。例えば。 SomeExceptionを取るハンドラは何かをキャッチします(普通は良い考えではありません)。IOExceptionを取るハンドラはIO例外を捕捉します。

結果として、コンパイラはどのタイプの例外を捕まえようとしているのかを推測することができないため、例のような「何もしない」ハンドラであいまい性の問題に遭遇するのは簡単です。これを簡単に修正する方法は、ハンドラ関数の型シグニチャを提供することです。

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

これはやや冗長かもしれませんが、代わりの方法は、handleを特化することです。あなたはIOの例外を処理したいとき

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

次に、あなただけのハンドラの型シグネチャを綴るすることなく、handleIOを使用することができます。

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.Exception`の代わりにをインポートします。廃止予定の警告が表示されますが、プログラムはコンパイルされます。

それとも、新しいインターフェイスを使用するので、同様に、ハンドラに明示的な署名を与えることができます。ここで、タイプにRWHが出てきた後に長すぎない

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