2016-05-19 1 views
3

Est-il possible de diviser un Shell dans la bibliothèque Turtle (Haskell) et de faire des choses différentes à l'une ou l'autre des divisions, de sorte que le Shell original ne soit exécuté qu'une seule fois?Haskell Turtle - fractionner un shell

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

Par exemple, comment faire

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

tels que lstree "/ tmp" ne ferait courir une fois. En particulier, je voudrais envoyer Shell 2 et Shell 3 à différents fichiers en utilisant la sortie.

Répondre

3

Vous ne pourrez pas diviser un Shell en deux shells séparés qui s'exécuteront simultanément, sauf s'il y a de la magie que je ne connais pas. Mais l'écriture de fichier est un fold sur le contenu d'un shell ou d'une autre succession de choses. Il est construit en turtle que vous pouvez toujours combiner de nombreux plis et de les faire en utilisant simultanément le exécuter matériel Control.Foldl - ici

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

Un obus est secrètement un FoldM IO a r -> IO r sous le capot de toute façon, c'est donc essentiellement runShell. Pour ce faire, nous devons obtenir le droit Shell et le droit combiné FoldM IO. L'idée des types Fold a b et FoldM m a b du package foldl est le pliage simultané.

Je pense que la meilleure façon d'obtenir la coquille droite est juste pour le retour lstree fois un FilePathensemble avec le résultat de testdir. Vous avez écrit essentiellement ceci:

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

Alors maintenant, nous pouvons obtenir un Shell (Bool, FilePath) de /tmp Cela a toutes les informations nos deux plis auront besoin, et donc que notre fois combiné aura besoin.

Ensuite, nous pourrions écrire un pli d'aide qui imprime le composant Text du FilePath à une poignée donnée:

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

Ensuite, nous pouvons utiliser ce Handle -> FoldM IO FilePath() pour définir deux FoldM IO (Bool, FilePath)(). Chacun écrira des choses différentes à différentes poignées, et nous pouvons les unir en un seul pli simultané avec <*. Ceci est un FoldM IO ... indépendant et peut être appliqué par ex. à une liste pure de type [(Bool, FilePath)] en utilisant L.fold et il va écrire des choses différentes de la liste aux différentes poignées. Dans notre cas, cependant, nous l'appliquerons au Shell (Bool, FilePath) que nous avons défini.La seule partie subtile de ceci est l'utilisation de L.handlesM pour imprimer seulement le deuxième élément, dans les deux cas, et seulement ceux filtrés comme des répertoires dans l'autre. Ceci utilise l'objectif _2 et filtered des bibliothèques d'objectifs. Cela pourrait probablement être simplifiée, mais voir ce que vous pensez:

{-#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 
+0

Ok, ça explique très bien comment le faire. La partie qui me manquait était de comprendre que l'écriture dans un fichier peut aussi être un pli utilisant 'L.sink'. Juste signifie que je dois explorer plus la puissance de Foldl! –

0

Il semble que vous cherchiez quelque chose comme async pour séparer vos obus du premier obus et attendre leur retour. async est une bibliothèque assez capable qui peut atteindre beaucoup plus que l'exemple ci-dessous, mais il fournit une solution assez simple à ce que vous vous demandez:

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 

Est-ce que vous cherchez?

+0

que je fais le traitement des flux, donc en fait je veux que tout arrive en synchronisation, ligne par ligne, donc je ne pense pas que async est la réponse ici . –