如何在Haskell中实现safeReadFile函数以产生Maybe String

时间:2018-08-04 09:26:52

标签: haskell types exception-handling io monads

我正在尝试在Haskell中实现安全的loadFile函数,该函数会捕获任何异常并产生Maybe String,但以下实现无法编译

 import System.IO         (readFile)
 import Control.Exception (catch, IOException)

 -- readFile :: FilePath -> IO String

 -- this compiles good
 safeReadFile :: FilePath -> IO (Either IOException String)
 safeReadFile p =
    (Right <$> readFile p) `catch`
    (\e -> pure $ Left e)

 -- this does not!
 safeReadFile' :: FilePath -> IO (Maybe String)
 safeReadFile' p =
    (Just <$> readFile p) `catch` 
    (\e -> pure Nothing)

有人可以解释为什么GCH提出以下问题吗?

  Ambiguous type variable ‘e0’ arising from a use of ‘catch’
  prevents the constraint ‘(GHC.Exception.Exception
                              e0)’ from being solved.
  Probable fix: use a type annotation to specify what ‘e0’ should be.
  These potential instances exist:
    instance GHC.Exception.Exception IOException
      -- Defined in ‘GHC.IO.Exception’
    ...plus 20 instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)

如何在e变量上应用必要的类型注释? Haskell文档没有任何提示:-(

1 个答案:

答案 0 :(得分:4)

您必须指定要捕获的异常类型。 safeLoadFile明确提及IOException,而safeLoadFile'没有提及。

尝试以下方法:

safeLoadFile' :: FilePath -> IO (Maybe String)
safeLoadFile' p =
    (Just <$> loadFile p) `catch` 
    ((\e -> pure Nothing) :: IOException -> IO (Maybe String))

或者找到一些类似的方法来注释变量e的类型。例如,如果您打开(\ (e :: IOException) -> ...),也可以使用ScopedTypeVariables

还有另一种选择:

safeLoadFile' :: FilePath -> IO (Maybe String)
safeLoadFile' p = (Just <$> loadFile p) `catch` handler
   where
   handler :: IOException -> IO (Maybe String)
   handler _ = pure Nothing