質問

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top