一次在文件系统上执行多项操作的正确方法是什么? [英] What's the right way of performing several operations on a filesystem at once?

查看:83
本文介绍了一次在文件系统上执行多项操作的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想知道一个文件是否存在,如果它是目录,则另外获取其内容.我可能会这样:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆