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.Csvhas material for handling headers, but I think that this question is really looking for a more sophisticated use ofPipes.awaitor elsePipes.next. Firstnext:nextis 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
Eithervalues are distraction here, I eliminateLeftvalues - the lines that don't parse - withP.concatnextdoes 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
produceris empty, I won't be able to declare this, as I do withNo lines!in the previous program.Note by the way that
showPipecan be defined asP.map show, or simply asP.show(but with the specialized type you add.)