Parsec doesn't let you do this directly out of the box, but you can achieve this using Parsec's public API as follows:
{-# LANGUAGE ScopedTypeVariables #-}
import Text.Parsec
changeState
:: forall m s u v a . (Functor m, Monad m)
=> (u -> v)
-> (v -> u)
-> ParsecT s u m a
-> ParsecT s v m a
changeState forward backward = mkPT . transform . runParsecT
where
mapState :: forall u v . (u -> v) -> State s u -> State s v
mapState f st = st { stateUser = f (stateUser st) }
mapReply :: forall u v . (u -> v) -> Reply s u a -> Reply s v a
mapReply f (Ok a st err) = Ok a (mapState f st) err
mapReply _ (Error e) = Error e
fmap3 = fmap . fmap . fmap
transform
:: (State s u -> m (Consumed (m (Reply s u a))))
-> (State s v -> m (Consumed (m (Reply s v a))))
transform p st = fmap3 (mapReply forward) (p (mapState backward st))
Note that it requires both forward and backward conversion between u
and v
. The reason is that first you need to translate your ambient state to the local state, run your inner parser, an then convert back.
ScopedTypeVariables
and local type signatures are there just for clarity — feel free to remove them if you like.