Parsec では、すぐにこれを行うことはできませんが、次のように Parsec のパブリック API を使用してこれを実現できます。
{-# 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))
u
との間の前方変換と後方変換の両方が必要であることに注意してくださいv
。その理由は、最初にアンビエント状態をローカル状態に変換し、内部パーサーを実行してから元に戻す必要があるためです。
ScopedTypeVariables
ローカル型のシグネチャはわかりやすくするためにあります。必要に応じて自由に削除してください。