3

I have this code:

import Pipes
import Pipes.Safe
import qualified Pipes.Prelude as P
import qualified Pipes.Safe.Prelude as P
import System.IO

import Data.Text as T
import Data.Text.IO as TIO
import qualified Pipes.Prelude.Text as T

someFunc :: IO ()
someFunc = runSafeT $
  P.withFile file1 ReadMode $ \file1Handle -> do
    file2 <- liftIO $ TIO.hGetLine file1Handle
    runEffect $
      for (P.zip (T.fromHandleLn file1Handle)
                 (T.readFileLn $ T.unpack file2))
          (\(l1,l2) -> do yield l2
                          yield l1)
      >-> T.stdoutLn

But it seems quite hacky, and I would like to be able to open the second file from within the pipe, from what I read in the first line of the first file. Any idea?

Yves Parès
  • 581
  • 5
  • 14
  • I have a feeling that's probably the cleanest code you are going to get. If you don't get an answer here try [pipes mailing list](https://groups.google.com/forum/#!forum/haskell-pipes) - and then post the answer back here. – ErikR Aug 24 '16 at 20:23

1 Answers1

2

There are I think a number of ways of doing this without mixing Data.Text.IO in with Pipes stuff. It is basically a zip, as you say. The natural way of finding out the first line of the first file is just to use next which, in this case, gives you the first line paired with the rest of lines:

someFunc_ = runSafeT $ runEffect $ do 
  e <- next (T.readFileLn file1)
  case e of
    Left r              -> return r
    Right (file2, rest) -> do 
      let other = T.readFileLn (T.unpack file2)
          amalgam = for (P.zip rest other) $ \(l1,l2) -> do
            yield l2 
            yield l1
      runEffect $ amalgam >-> T.stdoutLn

I think an approach that tried to do more 'inside a pipeline' would end up being more complicated than it was worth. In particular, you have to deal with the case where file1 turns out to empty. next is basically what you use to pattern match on a pipes Producer, so it's a fundamental pipes operation.

So, part for part, it's basically the same as a regular Text program like this

 someFunc = do
    ls <- fmap T.lines TIO.readFile file1 
    case ls of
      []         -> return ()
      file2:rest -> do 
        ls' <- fmap T.lines (TIO.readFile file1) 
        forM_ (zip ls ls') $ \(t1,t2) -> do
             TIO.putStrLn t1
             TIO.putStrLn t2

except that it streams properly.

Michael
  • 2,889
  • 17
  • 16