2014年2月11日火曜日

[Haskell] handle関数の例外処理部分で The type variable `e0' is ambiguous

Real World Haskell 9章 にならって、
ファイルサイズを取得するコードを書いたところ、

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


以下のようなエラーが発生した。
例外の型が複数定義されていて、あいまいなので、型を明示しろというエラーのようだ。

    No instance for (GHC.Exception.Exception e0)
      arising from a use of `handle'
    The type variable `e0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance GHC.Exception.Exception GHC.IO.Exception.ArrayException
        -- Defined in `GHC.IO.Exception'
      instance GHC.Exception.Exception GHC.IO.Exception.AssertionFailed
        -- Defined in `GHC.IO.Exception'
      instance GHC.Exception.Exception GHC.IO.Exception.AsyncException
        -- Defined in `GHC.IO.Exception'
      ...plus 15 others
    In the expression: handle (const (return Nothing))
    In the expression:
      handle (const (return Nothing))
      $ do { h <- openFile path ReadMode;
             size <- hFileSize h;
             hClose h;
             return (Just size) }
〜以下略〜

例外の型に応じた処理をしないのならば、どのような例外の型にもあうSomeExceptionがControl.Exceptionモジュールに定義されているので、それを使えば良い。

getFileSize path = handle (\(SomeException _) -> return Nothing) $ do 〜

最終的には、例外発生時でもファイルハンドルを必ず閉じるように、bracketを使って、以下のコードとなる。
getFileSize :: FilePath -> IO (Maybe Integer)
getFileSize path = handle (\(SomeException _) -> return Nothing) $ do
                     bracket (openFile path ReadMode) hClose $ \h -> do
                       size <- hFileSize h
                       return (Just size)


参考