一次在文件系统上执行多项操作的正确方法是什么? [英] What's the right way of performing several operations on a filesystem at once?
问题描述
假设我想知道一个文件是否存在,如果它是目录,则另外获取其内容.我可能会这样:
Suppose I want to know whether a file exists, and if it's a directory, additionally retrieve its contents. I may go as follows:
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"块之前,会发生什么?
-- And it kinda works. But I wonder: what would happen if the node gets unlinked along the way, say just before the last "do" block?
我不了解C,但是我的猜测是所有这些麻烦都将被1(一个)POSIX系统调用代替:opendir
要么让我读取目录内容,要么返回有意义的错误,我可以进行模式匹配反对.不过,这仅适用于POSIX兼容系统.
I don't know C, but my guess is that all this iffiness would be replaced by 1 (one) POSIX system call: opendir
will either let me read the directory contents or return a meaningful error I could pattern match against. That's only for POSIX-compliant systems, though.
在Haskell中,做这种事情的正确,惯用的,专业的方法是什么?我可以用System.Posix.Files
中的内容解决吗?与此相关的最新技术是什么?
What is the correct, idiomatic, professional way of doing things like this in Haskell? Do I address it with the stuff from System.Posix.Files
? What is the state of the art around this?
后记
我本来可以强制转换listDirectory
,并且模式匹配错误(根据@Ryan的建议),但是我有点可疑,因为在ENOENT
和<的情况下,它显然都可以说NoSuchThing
. c5>.描述很少,行为没有明确说明,我也不想阅读任何保证.
I could have just casted listDirectory
, and pattern matched on the error (as per suggestion from @Ryan), but I am kind of suspicious because it can apparently say NoSuchThing
in case of both ENOENT
and ENOTDIR
. The description is scarce, the behaviour is not spelled out, and I don't want to read any guarantees into it.
推荐答案
但是我想知道:如果节点在此过程中未链接,例如在最后一个"do"块之前,会发生什么?
But I wonder: what would happen if the node gets unlinked along the way, say just before the last "do" block?
您将获得一个例外,在这种情况下,这没什么大不了的.您正在执行IO,并且可以处理它.我认为您不会找到与故障预防平台无关的解决方案.
You'll get an exception, and that's no big deal in this context. You are performing IO and can handle it. I don't think you'll find a fail-proof platform agnostic solution.
您如何制作一个有用的包装器monad来保持代码的可读性?可能比您喜欢的样板好(在这种情况下,只需用catch
包装整个函数调用),但是对于较大部分的代码来说,可能会很不错:
How about you make a useful wrapper monad to keep the code readable? It might be more boilerplate than you like (in which case just wrap the whole function call with catch
) but for larger portions of code can be quite nice:
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
结果.您可以这样使用它:
The M
monad allows you to capture all the ugly exceptions along with your program logic and bundle it up into a single Either
result. You'd use it as such:
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
提供更多信息,以便在发生故障时/如果将失败的结果与操作中可能发生的事情联系在一起,但这通常是过大的.
You could enhance things, such as provide myIO
with more information so if/when things fail you can tie the result to where in your operation things took a nose dive but that's usually overkill.
我们可以使用whenM(输入未经测试)进一步清理:
We could clean things up further with whenM (typed not tested):
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
这篇关于一次在文件系统上执行多项操作的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!