I'm looking for an easy way to combine two parts of ParsecT code that have the same stream and monad, but different user state and outcome. Essentially a function like this would be nice:
withUserState :: u -> ParsecT s u m a -> ParsecT s v m a
The thing is, that the user state is really helpful in some cases, but I need different states at different times and don't want to make the states type any bigger. Do I have to modify State somehow to achieve this, or is there already a function for it that I can't find at the moment?
Edit:
I think an alternative would be something like
changeUserState :: (u -> v) -> ParsecT s u m a -> ParsecT s v m a
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.
You can't do this as the >>=
operator has the type
ParsecT s u m a -> (a -> ParsecT s u m b) -> ParsecT s u m b
and (<*>)
as
ParsecT s u m (a -> b) -> ParsecT s u m a -> ParsecT s u m b
The s
variable is universally quantified, but must match with both terms. Without >>=
or <*>
you can use no applicative or monadic functions. This means you'd have absolutely no way to combine any parsers with different states. The best way to do this is just
data PotentialStates = State1 ...
| State2 ...
| State3 ...
and then just work with those instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With