Haskell乌龟-拆分一个shell

Haskell Turtle - split a shell

是否可以在 Turtle 库 (Haskell) 中拆分 Shell 并对 shell 的任一拆分执行不同的操作,以便原始 Shell只有 运行 一次吗?

             /---- shell2
---Shell1 --/
            \
             \-----shell3

比如,怎么做

do
  let lstmp = lstree "/tmp"
  view lstmp
  view $ do
    path <- lstmp
    x <- liftIO $ testdir path
    return x

这样 lstree "/tmp" 只会 运行 一次。

具体来说,我想使用输出将 Shell 2 和 Shell 3 发送到不同的文件。

听起来您正在寻找类似 async 的东西来将您的 shell 从第一个 shell 中分离出来,然后等待它们 return。 async 是一个非常强大的库,它可以实现比下面的示例更多的功能,但它提供了一个非常简单的解决方案来满足您的要求:

import Control.Concurrent.Async
import Turtle.Shell
import Turtle.Prelude

main :: IO ()
main = do
  let lstmp1 = lstree "/tmp"
  let lstmp2 = lstree "/etc"
  view lstmp1
  view lstmp2
  job1 <- async $ view $ do
    path <- lstmp1
    x <- liftIO $ testdir path
    return x
  job2 <- async $ view $ do
    path <- lstmp2
    x <- liftIO $ testdir path
    return x
  wait job1
  wait job2

这是您要找的吗?

您将无法将 Shell 拆分为两个单独的 shell 同时 运行,除非有一些我不知道的魔法。但是文件写入是 fold 超过 shell 或其他一些连续事物的内容。它内置于 turtle 中,您可以随时组合多个折叠并使用 Control.Foldl material 同时制作它们 运行 - 这里

foldIO ::  Shell a -> FoldM IO a r -> IO r  -- specializing

A shell 无论如何都是秘密的 FoldM IO a r -> IO r,所以这基本上是 runShell。为此,我们需要获得正确的 Shell 和正确的组合 FoldM IO。来自 foldl 包的 Fold a bFoldM m a b 类型的整个想法是同时折叠。

我认为获得正确 shell 的最简单方法就是让 lstree 折叠 return 一个 FilePath 在一起 结果为 testdir。你基本上是这样写的:

withDirInfo :: FilePath -> Shell (Bool, FilePath)
withDirInfo tmp = do
    let lstmp = lstree tmp
    path <- lstmp
    bool <- liftIO $ testdir path
    return (bool, path)

所以现在我们可以从 /tmp 得到一个 Shell (Bool, FilePath) 这有我们两个折叠需要的所有信息,因此我们的组合折叠需要。

接下来我们可能会编写一个辅助折叠,将 FilePathText 组件打印到给定句柄:

sinkFilePaths :: Handle -> FoldM IO FilePath ()
sinkFilePaths handle = L.sink (T.hPutStrLn handle . format fp)

那我们可以用这个Handle -> FoldM IO FilePath ()定义两个FoldM IO (Bool, FilePath) ()。每个都会将不同的东西写入不同的句柄,我们可以使用 <* 将它们合并为一个同时折叠。这是一个独立的 FoldM IO ...,可以应用于例如使用 L.fold[(Bool, FilePath)] 类型的纯列表,它会将列表中的不同内容写入不同的句柄。不过,在我们的例子中,我们会将其应用于我们定义的 Shell (Bool, FilePath)

其中唯一微妙的部分是使用 L.handlesM 仅打印第二个元素,在这两种情况下,在另一种情况下仅打印作为目录过滤的元素。这使用镜头库中的 _2 镜头和 filtered。这可能会被简化,但看看你的想法:

{-#LANGUAGE OverloadedStrings #-}
import Turtle
import qualified Control.Foldl as L
import qualified System.IO as IO
import Control.Lens (_2,filtered)
import qualified Data.Text.IO as T

main = IO.withFile "tmpfiles.txt" IO.WriteMode $ \h ->
       IO.withFile "tmpdirs.txt" IO.WriteMode $ \h' -> do
        foldIO (withDirInfo "/tmp") (sinkFilesDirs h h') 

withDirInfo :: Turtle.FilePath -> Shell (Bool, Turtle.FilePath)
withDirInfo tmp = do
    let lstmp = lstree tmp
    path <- lstmp
    bool <- liftIO $ testdir path
    return (bool, path)

sinkFilePaths :: Handle -> FoldM IO Turtle.FilePath ()
sinkFilePaths handle = L.sink (T.hPutStrLn handle . format fp)

sinkFilesDirs  :: Handle -> Handle -> FoldM IO (Bool, Turtle.FilePath) ()
sinkFilesDirs h h' = allfiles <* alldirs where

  allfiles :: L.FoldM IO (Bool, Turtle.FilePath) ()
  allfiles = L.handlesM _2 (sinkFilePaths h)
  -- handle the second element of pairs with sinkFilePaths

  alldirs :: FoldM IO (Bool, Turtle.FilePath) ()
  alldirs = L.handlesM (filtered (\(bool,file) -> bool) . _2) (sinkFilePaths h')
 -- handle the second element of pairs where the first element
 -- is true using sinkFilePaths