First let's use MonadPlus
instead of []
in the parser. It will make it more generic and clarify the code a bit (we won't have so many nested []
s):
newtype Parser a m b = P { runP :: [a] -> m (b, [a]) }
I suggest you to change the signature of your subroutines. What you need is to:
- signal if a subroutine needs more input or not, and
- keep the state somewhere.
This can be easily accomplished by this type signature:
newtype Sub a b = Sub { runSub :: Either (a -> Sub a b) [b] }
A subroutine either yields a result, or requests a new input and produces a new subroutine. This way, you can keep whatever state you need by passing it into the returned subroutine. The conversion function will then look like:
withf :: (MonadPlus m) => Sub a b -> Parser a m b
withf start = P $ f (runSub start)
where
f (Right bs) xs = msum [ return (b, xs) | b <- bs ]
f (Left r) [] = mzero -- No more input, can't proceed.
f (Left r) (x:xs) = f (runSub (r x)) xs
Update: Another approach we could take is to realize that the parser is actually a StateT
transformer, whose state is [a]
:
type Parser a m b = StateT [a] m b
runP :: (Monad m) => Parser a m b -> [a] -> m (b, [a])
runP = runStateT
Indeed, runP
is exactly runStateT
!
This way, we get a Monad
instance for Parser
for free. And now we can split our task into smaller blocks. First, we create a parser that consumes one input, or fails:
receive :: (MonadPlus m) => Parser a m a
receive = get >>= f
where
f [] = mzero -- No more input, can't proceed.
f (x:xs) = put xs >> return x
and then use it to describe withf
:
withf :: (MonadPlus m) => Sub a b -> Parser a m b
withf start = f (runSub start)
where
f (Right bs) = msum (map return bs)
f (Left r) = receive >>= f . runSub . r
Notice that if m
is a MonadPlus
then also StateT s m
is a MonadPlus
, so we can use directly mzero
and msum
with Parser
.