首页 > 解决方案 > 如何在 Haskell 中实现产生 Maybe String 的 safeReadFile 函数

问题描述

我正在尝试loadFile在 Haskell 中实现一个安全函数,该函数捕获任何异常并产生 aMaybe 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 文档没有提供任何线索:-(

标签: haskelltypesexception-handlingiomonads

解决方案


您必须指定要捕获的异常类型。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

推荐阅读