Search code examples
haskellattoparsechaskell-pipes

Skipping first line in pipes-attoparsec


My types:

data Test = Test {
 a :: Int,
 b :: Int
} deriving (Show)

My parser:

testParser :: Parser Test
testParser = do
  a <- decimal
  tab
  b <- decimal
  return $ Test a b

tab = char '\t'

Now in order to skip the first line, I do something like this:

import qualified System.IO as IO    

parser :: Parser Test
parser = manyTill anyChar endOfLine *> testParser

main = IO.withFile testFile IO.ReadMode $ \testHandle -> runEffect $
         for (parsed (parser <* endOfLine) (fromHandle testHandle)) (lift . print)

But the above parser function makes every alternate link skip (which is obvious). How to only skip the first line in such a way that it works with Pipes ecosystem (Producer should produce a single Test value.) This is one obvious solution which I don't want (the below code will only work if I modify testParser to read newlines) because it returns the entire [Test] instead of a single value:

tests :: Parser [Test]
tests = manyTill anyChar endOfLine *>
        many1 testParser

Any ideas to tackle this problem ?


Solution

  • You can drop the first line efficiently in constant space like this:

    import Lens.Family (over)
    import Pipes.Group (drops)
    import Pipes.ByteString (lines)
    import Prelude hiding (lines)
    
    dropLine :: Monad m => Producer ByteString m r -> Producer ByteString m r
    dropLine = over lines (drops 1)
    

    You can apply dropLine to your Producer before you parse the Producer, like this:

    main = IO.withFile testFile IO.ReadMode $ \testHandle -> runEffect $
        let p = dropLine (fromHandle testHandle)
        for (parsed (parser <* endOfLine) p) (lift . print)