首页 文章

“句柄”功能和真实世界Haskell

提问于
浏览
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;
                   .... }

这里出了什么问题?为什么不编译?

2 回答

  • 4

    在RWH出现后不久,异常接口被更改为支持更灵活的处理程序,其中处理程序的类型确定它将捕获哪些异常 . 例如 . 需要 SomeException 的处理程序将捕获任何内容(通常不是一个好主意),而采用 IOException 的处理程序将只捕获IO异常 .

    因此,很容易遇到类似于示例中的“无所事事”处理程序的歧义问题,因为编译器无法推断出您尝试捕获的异常类型 . 解决此问题的一种简单方法是为处理程序函数提供类型签名 .

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

    虽然,这可能有点冗长 . 另一种解决方案是专门化 handle .

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

    然后,只要您想处理IO异常,就可以使用 handleIO ,而不必拼出处理程序的类型签名 .

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

    第三种选择是使用 ScopedTypeVariables 扩展,其中(除其他外)允许您仅为函数的参数提供类型注释,允许推断其余部分 .

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

    RWH相当陈旧 . handle 函数签名在GHC 6.10左右发生了变化 .

    要使用旧版本,请导入 Control.OldException 而不是Control.Exception` . 您将获得弃用警告,但程序将编译 .

    或者您可以使用新接口并为处理程序提供显式签名,如下所示:

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

相关问题