假设我想知道文件是否存在,如果它是一个目录,还要检索其内容。我可以如下:
browseSimple :: FilePath -> IO (Either FilePath [FilePath])
browseSimple x = do
isAvailable <- doesPathExist x
if not isAvailable then error $ "File not found: " ++ x else do
isFile <- doesFileExist x
if isFile then return $ Left x else do
isDirectory <- doesDirectoryExist x
if not isDirectory then error $ "Unknown filesystem node: " ++ x else do
listing <- listDirectory x
return $ Right ((x </>) <$> listing)
-- ^
-- λ browseSimple "."
-- Right [..."./Filesystem.hs"...]
- 它有点有效。但是我想知道:如果节点在路上被取消链接会发生什么,比如在最后一个“do”块之前呢?
我不知道C,但我的猜测是所有这些iffiness将被1(一)POSIX系统调用取代:opendir
将让我读取目录内容或返回一个有意义的错误我可以模式匹配。但这只适用于符合POSIX标准的系统。
在Haskell中执行此类操作的正确,惯用,专业方式是什么?我是否使用System.Posix.Files
中的内容解决了这个问题?这周围的最新技术是什么?
postscriptum
我本来可以投放listDirectory
,并在错误上匹配模式(根据@Ryan的建议),但我有点怀疑,因为如果两者{NoSuchThing
显然可以说ENOENT
{1}}和ENOTDIR
。描述很少,行为没有拼写出来,我不想读任何保证。
答案 0 :(得分:5)
但是我想知道:如果节点在路上被取消链接会发生什么,比如在最后一个“do”块之前呢?
你会得到一个例外,在这种情况下这没什么大不了的。您正在执行IO并且可以处理它。我认为你不会找到一个防故障平台无关的解决方案。
如何制作一个有用的包装器monad来保持代码的可读性?它可能比你喜欢的样板(在这种情况下只用catch
包装整个函数调用)但是对于更大部分的代码可能非常好:
data MyErr = CaughtException SomeException
-- ^ You might want to just catch
-- specific types of exceptions here
| MyErr String
type M = ExceptT MyErr IO
myErr :: String -> M a
myErr = throwE . MyErr
myIO :: IO a -> M a
myIO io = ExceptT (catch (Right <$> io) (pure . Left . CaughtException))
M
monad允许您捕获所有丑陋的异常以及程序逻辑,并将其捆绑到单个Either
结果中。你可以这样使用它:
browseSimple :: FilePath -> IO (Either MyErr [FilePath])
browseSimple x = runExceptT $ do
isAvailable <- myIO $ doesPathExist x
when (not isAvailable) (myErr $ "File not found: " ++ x)
isFile <- myIO $ doesFileExist x
when isFile (myErr x)
isDirectory <- myIO $ doesDirectoryExist x
when (not isDirectory) (myErr $ "Unknown filesystem node: " ++ x)
listing <- myIO $ listDirectory x
return ((x </>) <$> listing)
你可以增强一些东西,比如提供myIO
更多的信息,所以如果/当事情失败你可以将结果绑定到你的操作中的东西,但这通常是过度的。
我们可以使用whenM(键入未测试)进一步清理:
whenM io m = myIO io >>= \b -> when b m
browseSimple2 :: FilePath -> IO (Either MyErr [FilePath])
browseSimple2 x = runExceptT $ do
whenM (not <$> doesPathExist x)
(myErr $ "File not found: " ++ x)
whenM (doesFileExist x)
(myErr x)
whenM (not <$> doesDirectoryExist x)
(myErr $ "Unknown filesystem node: " ++ x)
myIO $ (x </>) <$> listDirectory x