8

ストリームとモナドが同じで、ユーザーの状態と結果が異なる 2 つの ParsecT コードの部分を組み合わせる簡単な方法を探しています。基本的に、このような関数はいいでしょう:

withUserState :: u -> ParsecT s u m a -> ParsecT s v m a

問題は、場合によってはユーザー状態が非常に役立つことですが、さまざまな時期にさまざまな状態が必要であり、状態の型をこれ以上大きくしたくないということです。これを達成するために何らかの方法で State を変更する必要がありますか、それとも現時点では見つけられない関数が既に存在しますか?

編集:

代替案は次のようなものになると思います

changeUserState :: (u -> v) -> ParsecT s u m a -> ParsecT s v m a
4

2 に答える 2

10

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ローカル型のシグネチャはわかりやすくするためにあります。必要に応じて自由に削除してください。

于 2013-07-31T12:12:16.203 に答える