2012-05-14 15 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))