I am reading a csv file with pipes-csv library. I want to read first line and read the rest later. Unfortunately after Pipes.Prelude.head function returns. pipe is being closed somehow. Is there a way to read head of the csv first and read the rest later.
import qualified Data.Vector as V
import Pipes
import qualified Pipes.Prelude as P
import qualified System.IO as IO
import qualified Pipes.ByteString as PB
import qualified Data.Text as Text
import qualified Pipes.Csv as PCsv
import Control.Monad (forever)
showPipe :: Proxy () (Either String (V.Vector Text.Text)) () String IO b
showPipe = forever $ do
x::(Either String (V.Vector Text.Text)) <- await
yield $ show x
main :: IO ()
main = do
IO.withFile "./test.csv"
IO.ReadMode
(\handle -> do
let producer = (PCsv.decode PCsv.NoHeader (PB.fromHandle handle))
headers <- P.head producer
putStrLn "Header"
putStrLn $ show headers
putStrLn $ "Rows"
runEffect ( producer>->
(showPipe) >->
P.stdoutLn)
)
If we do not read the header first, we can read whole csv without any problem:
main :: IO ()
main = do
IO.withFile "./test.csv"
IO.ReadMode
(\handle -> do
let producer = (PCsv.decode PCsv.NoHeader (PB.fromHandle handle))
putStrLn $ "Rows"
runEffect ( producer>->
(showPipe) >->
P.stdoutLn)
)
Pipes.Csv
has material for handling headers, but I think that this question is really looking for a more sophisticated use ofPipes.await
or elsePipes.next
. Firstnext
:next
is the basic way of inspecting a producer. It is sort of like pattern matching on a list. With a list the two possibilities are[]
andx:xs
- here they areLeft ()
andRight (headers, rows)
. The latter pair is what you are looking for. Of course an action (here inIO
) is needed to get one's hands on it:Since the
Either
values are distraction here, I eliminateLeft
values - the lines that don't parse - withP.concat
next
does not act inside a pipeline, but directly on theProducer
, which it treats as a sort of "effectful list" with a final return value at the end. The particular effect we got above can of course be achieved withawait
, which acts inside a pipeline. I can use it to intercept the first item that comes along in a pipeline, do some IO based on it, and then forward the remaining elements:The difference is just that if
producer
is empty, I won't be able to declare this, as I do withNo lines!
in the previous program.Note by the way that
showPipe
can be defined asP.map show
, or simply asP.show
(but with the specialized type you add.)