1

我正在尝试编写一个简单的函数来安全地读取文件(如果它存在)并且如果文件不存在则什么都不做:

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 readFileis readFile :: FilePath -> IO String。例如返回的函数IO String(并且IO String与 不同IO ()

将签名更改为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 中编写这种错误安全、灵活的代码?更具体地说,我必须对这个函数做出什么改变才能让它同时处理成功案例和失败案例?

4

1 回答 1

8

你需要弄清楚你希望你的函数实际做什么。

如果它成功读取文件,您希望它以字符串形式返回内容。

如果它失败了,你真正想要它做什么?返回一个空字符串或其他一些后备内容?然后你可以在第一种情况下将更改return ()为。return ""handleExists

但是如果你想在返回类型中指出错误,那么你需要返回一个不同于 just 的类型String。正如 Carsten 所说,您可以返回 aMaybe StringJust theString为成功和Nothing错误而付出。Either或者,如果您想要一些错误消息,您可以返回一个。

我觉得对于这个特定的功能,Maybe String最有意义,因为你只捕获不存在的文件并重新抛出其他错误。那么你的代码需要看起来像这样:

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在 aJust中以满足类型要求,并在错误情况下返回Nothing而不是 unit。

于 2016-01-08T10:11:47.683 回答