我正在尝试编写一个简单的函数来安全地读取文件(如果存在),如果文件不存在则不执行任何操作:
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 readFile
的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中编写这种错误安全,灵活的代码?更具体地说,我必须对此功能做出哪些改变才能让它同时处理成功案例和失败案例?
你需要找出你希望你的功能实际做什么.
如果它成功读取文件,您希望它以字符串形式返回内容.
如果它失败了,你真的想要它做什么?返回一个空字符串或其他一些后备内容?然后你可以在第一种情况下改变return ()
to .return ""
handleExists
但是如果你想在返回类型中指出错误,那么你需要返回一个不同的类型String
.正如卡斯滕所说,你可以回归Maybe String
并Just 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
在a中Just
以满足类型要求,并在错误情况下返回Nothing
而不是单位.