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

2024-02-03

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

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

在 Haskell 中做这样的事情的正确、惯用、专业的方法是什么?我可以用来自的东西来解决这个问题吗?System.Posix.Files?这方面的最新技术是什么?


后记

我本可以直接投射listDirectory,并且模式匹配了错误(根据@Ryan的建议),但我有点怀疑,因为它显然可以说NoSuchThing如果两者都有ENOENT and ENOTDIR。描述很少,行为没有详细说明,我不想在其中读到任何保证。


但我想知道:如果节点一路上断开链接,比如在最后一个“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))

The Mmonad 允许您捕获所有丑陋的异常以及您的程序逻辑并将其捆绑到一个单一的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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 如何处理“恐慌:不可能的事情发生了”并在 Haskell 中继续

    我有以下代码 它使用 GHC API 加载模块并获取表达式的类型 typeObjects String gt String gt IO Type typeObjects modules objects do defaultErrorHand
  • 管道中缺少 ResourceT 实例

    我在尝试使用时遇到奇怪的错误ResourceT http hackage haskell org package conduit 1 0 9 1 docs Data Conduit html t 3aResourceT来自管道 1 0 9
  • 使用 Haskell 将函数注入到 Java .class 文件中

    我使用 Haskell 编写了一个 Java 字节码解析器 它工作得很好 然而下一步让我完全难住了 我的 Haskell 程序需要修改 class 文件 以便在执行时 Java 程序打印 输入 此处的方法名称 在执行方法之前 并且 退出 此
  • 为什么 Haskell 中的点是从右向左排列的?

    如果我们有两个函数 f and g 然后在哈斯克尔h f g相当于h x f g x IE 这些函数从右到左应用于输入 有什么根本原因可以解释为什么它是从右到左 而不是从左到右吗 IE 他们为什么不做h f g相当于h x g f x 反而
  • Haskell 中实例声明的参数顺序切换

    我想进行实例声明 但自由类型变量不是最后一个变量 例如 我有一个类声明 class Poppable m where tryPop m a gt Maybe a m a 现在我想让 Q PSQ 优先级队列 成为 Poppable 的实例 具
  • 在 Haskell 中创建 100 万个线程需要多长时间?

    据我了解 Haskell 有绿色线程 但它们的重量有多轻 是否可以创建100万个线程 或者 100 000 个线程需要多长时间 from here http www reddit com r programming comments a4n
  • Haskell:从后面访问列表

    今天我开始学习Haskell 我对函数式语言有点陌生 而且我非常喜欢 Haskell 然而 我有一个关于它的设计的问题困扰着我 从我到目前为止的理解来看 访问列表后面的元素似乎比访问前面的元素要复杂得多 类似于xs x where xs a
  • 用纯函数式语言保持状态

    我正在尝试弄清楚如何执行以下操作 假设您正在开发直流电机的控制器 您希望让它以用户设置的特定速度旋转 def set point ref sp 90 while true let curr read speed controller set
  • 不明白这个 haskell 代码中的内容

    我有一些 Haskell 代码 我正在尝试完成它 但我不明白其中发生了什么 type Bag a a gt Int emptyB Bag a emptyB e gt 0 countB Eq a gt Bag a gt a gt Int co
  • 加快 GHC 中的编译速度

    除了 O0 这可以加快编译时间吗 如果生成的程序未被优化也没关系 实际上我只想经常快速地对大型 haskell 包进行类型检查 Flag fno code极大地加快了编译速度 但无法使用它 因为该程序使用了 TemplateHaskell
  • 为连续可测量的现象创建行为

    我想创建一个Behavior t a从一个IO a 其预期语义是每次行为发生时都会运行 IO 操作sampled language FlexibleContexts import Reflex Dom import Control Mona
  • 为什么 Kleisli 不是 Monoid 的一个实例?

    如果您希望附加两个类型 a gt m b 的函数 以便只得到一个附加两个结果的相同类型的函数 您可以使用 Kleisli 来执行此操作 instance Monad m Monoid b gt Monoid Kleisli m a b wh
  • Haskell 中存在量化值的列表

    我想知道为什么这段代码不进行类型检查 LANGUAGE ScopedTypeVariables Rank2Types RankNTypes OPTIONS fglasgow exts module Main where foo forall
  • 记录语法和求和类型

    我有关于 Haskell 中的总和类型的问题 我想创建一个由两个或多个其他类型组成的总和类型 并且每个类型可能包含多个字段 一个简单的例子是这样的 data T3 T1 a Int b Float T2 x Char deriving Sh
  • 在 Haskell 中使用 Maybe 类型

    我正在尝试利用 Haskell 中的 Maybe 类型 我有一个查找返回 Maybe 的键 值元组 如何访问 Maybe 包装的数据 例如 我想将 Maybe 包含的整数与另一个整数相加 或者 您可以进行模式匹配 case maybeVal
  • 不明确的类型变量

    相关我之前关于遍历数据结构的问题 https stackoverflow com questions 1855371 avoiding boilerplate when dealing with many unrelated types 当
  • 数据记录的类约束

    我有一个data type data BuildException a KillBuild JobID a Stage FailBuild JobID a Stage CancelBuild JobID a Stage StopBuild
  • Haskell 程序查找列表中元素的位置

    我需要编写一个函数来查找列表中一个特定元素的位置 我是这样写的 findPos list elt list 1 head list elt 0 otherwise 1 findPos tail list elt 但是如果列表中元素重复怎么办
  • Haskell 乘加运算的数学性能

    我正在用 Haskell 编写一个游戏 我当前在 UI 上的传递涉及大量几何图形的程序生成 我目前专注于识别一项特定操作的性能 C ish 伪代码 Vec4f multiplier addend Vec4f vecList for int
  • 当您包含导入 Gloss 的项目时,“stack ghci”会失败

    如果您在 Stack 项目中导入 Gloss 并使用stack ghci 您会收到以下错误 GHCi version 7 10 2 http www haskell org ghc for help

随机推荐