I've defined a parser using Parsec, which has type Parsec Text () a
for some a
. I've also got a "deal with this chunk" function, which writes the thing I've parsed to a file and has type a -> IO ()
. The file format that it's parsing means that we get back to "top level" reasonably frequently.
Is there a way to take my original parser and "lift it" into the IO monad? I'm imagining something with the following type signature:
liftParser :: Parsec Text () a -> (a -> IO ()) -> ParsecT Text () IO ()
where the first argument is the pure parser and the second is the "do something with the thing I parsed" function.
Obviously, I can bodge together what I need by redefining my original parser in IO too, but that means my unit tests look horrible, and it just feels like the wrong approach.
Also, I can't do something crazy like calling runParserT
because that would drop the source position information - if there's an error on line 1000 of the input, I'd like the error message to say so.
So is there a way to do this and, if so, how? Also, is this a sensible thing to do? I imagine that I'm at least managing to avoid accumulating the output data. And, assuming that I manage something like this, should I expect Parsec
to manage to discard the input data that it's already dealt with?
ParsecT Text () m
, leavingm
undecided. Then your pure tests can test it onIdentity
, but you can instantiate atIO
when you need to. - luquirunParserT
for whichever reason, you can simply usesetPosition
to set Parsec's idea of the current source location (without skipping in the stream). - that other guy