Haskell 安全 IO

Haskell Safe IO

我正在尝试编写一个简单的函数来安全地读取文件(如果它存在)并且如果文件不存在则不执行任何操作:

safeRead :: String -> IO ()
safeRead path = readFile path `catch` handleExists
  where handleExists e
          | isDoesNotExistError e = return ()
          | otherwise = throwIO e

这在编译时失败

Couldn't match type ‘[Char]’ with ‘()’
Expected type: IO ()
  Actual type: IO String
In the first argument of ‘catch’, namely ‘readFile path’
In the expression: readFile path `catch` handleExists

这是有道理的,因为 :t readFilereadFile :: FilePath -> IO String。例如 returns IO String 的函数(并且 IO StringIO () 不同)

正在将签名更改为 String -> IO String

Couldn't match type ‘()’ with ‘[Char]’
Expected type: IOError -> IO String
  Actual type: IOError -> IO ()
In the second argument of ‘catch’, namely ‘handleExists’
In the expression: readFile path `catch` handleExists

这也是有道理的,因为 handleExists 的类型是 IO ()

为了节省每个人的查找时间,catch 被导入: import Control.Exception catch 的签名是: catch :: Exception e => IO a -> (e -> IO a) -> IO a

我真正的问题是,如何在 Haskell 中编写这种错误安全、灵活的代码?更具体地说,我必须对此函数进行哪些更改才能使其同时处理成功案例和失败案例?

您需要弄清楚您希望函数实际执行的操作。

如果它成功读取文件,您希望它return将内容作为字符串。

如果它失败了,你到底想让它做什么? Return 空字符串或其他一些后备内容?然后你可以在 handleExists.

的第一种情况下将 return () 更改为 return ""

但是如果你想在 return 类型中指出错误,那么你需要 return 一个不同于 String 的类型。正如 Carsten 所说,您可以 return a Maybe String 并给出 Just theString 表示成功,Nothing 表示错误。或者,如果您想要一些错误消息,您可以 return 一个 Either

我觉得对于这个特定的函数,Maybe String 最有意义,因为您只捕获文件的 non-presence 并重新抛出其他错误。那么您的代码需要如下所示:

safeRead :: String -> IO (Maybe String)
safeRead path = (fmap Just $ readFile path) `catch` handleExists
  where
    handleExists :: IOException -> IO (Maybe String)
    handleExists e
      | isDoesNotExistError e = return Nothing
      | otherwise = throwIO e

这里我们将 readFile 的结果包装在 Just 中以满足类型要求,并且在错误情况下 return Nothing 而不是 unit.