gpt4 book ai didi

haskell - 一次在文件系统上执行多个操作的正确方法是什么?

转载 作者:行者123 更新时间:2023-12-02 12:28:26 25 4
gpt4 key购买 nike

假设我想知道一个文件是否存在,如果它是一个目录,则另外检索它的内容。我可能会这样:

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” block 之前,会发生什么?

我不懂 C,但我的猜测是,所有这些不确定性都将被 1(一)个 POSIX 系统调用所取代:opendir 要么让我读取目录内容,要么返回一个有意义的值我可以进行模式匹配的错误。不过,这只适用于 POSIX 兼容的系统。

在 Haskell 中做这样的事情的正确、惯用、专业的方法是什么?我是否使用 System.Posix.Files 中的内容来解决这个问题?这方面的最新技术是什么?


后记

我本可以直接转换listDirectory,并根据错误匹配模式(根据@Ryan的建议),但我有点怀疑,因为它显然可以说NoSuchThing 如果是 ENOENTENOTDIR。描述很少,没有详细说明行为,而且我不想在其中阅读任何保证。

最佳答案

But I wonder: what would happen if the node gets unlinked along the way, say just before the last "do" block?

你会得到一个异常,在这种情况下这没什么大不了的。您正在执行 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

关于haskell - 一次在文件系统上执行多个操作的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48699422/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com