13

私は 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;
                   .... }

ここで何がうまくいかないのですか?なぜコンパイルされないのですか?

4

2 に答える 2

27

RWH が登場して間もなく、例外インターフェイスが変更され、より柔軟なハンドラーをサポートするようになりました。ハンドラーのタイプによって、キャッチする例外が決まります。たとえば、取るハンドラーはSomeException何でもキャッチしますが (通常は良い考えではありません)、受け取るハンドラーはIOExceptionIO 例外のみをキャッチします。

この結果として、コンパイラはキャッチしようとしている例外の種類を推測できないため、例のような「何もしない」ハンドラーであいまいな問題が発生しやすくなります。これを修正する簡単な方法は、ハンドラー関数に型シグネチャを提供することです。

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

ただし、これは多少冗長になる可能性があります。別の解決策は、特化することですhandle

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

次に、handleIOIO 例外を処理したいときはいつでも使用でき、ハンドラーの型シグネチャを詳しく説明する必要はありません。

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

3 番目のオプションは、ScopedTypeVariables拡張機能を使用することです。これにより、(とりわけ) 関数の引数のみに型注釈を提供し、残りを推論できるようになります。

{-# LANGUAGE ScopedTypeVariables #-}
saferFileSize path = handle (\(_ :: IOException) -> return Nothing) $ do ...
于 2012-05-14T07:35:45.917 に答える
4

RWHはかなり古いです。handle機能シグネチャはGHC6.10かそこらで変更されました。

古いバージョンを使用するControl.OldExceptionには、Control.Exception`の代わりにインポートします。非推奨の警告が表示されますが、プログラムはコンパイルされます。

または、新しいインターフェイスを使用して、次のようにハンドラーに明示的な署名を与えることができます。

((\ _ -> return Nothing) :: IOException -> IO (Maybe Integer))
于 2012-05-14T07:14:44.643 に答える