While playing around with parsing based on text-icu's BreakIterator
, I've got stuck on implementing a function like this
conditionalParser :: (a -> Bool) -> Parser a -> Parser a -> Parser a -> Parser a
conditionalParser f a b c = do
a' <- a
if f a'
then b
else c
but with a type
conditionalParserIO :: (a -> Bool) -> Parser (IO a) -> Parser (IO a) -> Parser (IO a) -> Parser (IO a)
Is it possible without doing unsafePerformIO
?
So far I could only get to some nested do
s with the final returned type being Parser (IO (Parser (IO a)))
, but without any idea how to collapse them.
I think what you want is to use ParsecT
instead of Parser
.
conditionalParserM :: Monad m => (a -> Bool) -> ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
conditionalParserM f a b c = do
a' <- a
if f a' then b else c
This function works with all types of Monad
s, not just IO
.
I suppose it's possible to convert from a ParsecT s u IO a
to a Parser (IO a)
using runParsecT
, depending on which Parser
(this or this?) you're using. However, I would recommend that you just restructure your code to work with ParsecT
instead.
Clarification
conditionalParserM
can't be used as a replacement for conditionalParserIO
. I'm suggesting that you need to change how your program works, because attempting to do what your doing (without unsafePerformIO
, which you should almost never use) is impossible.
You're looking to compose parsers based on the result of an IO operation, which means that the parser itself will perform side effects when it is run. In order to encapsulate this in the type, you need to use a monad transformer instead.
So, to use conditionalParserM
, you need to restructure your code to work with ParsecT
instead of Parser
.