一种简单的方法来改变Parsec用户状态的类型?

Jak*_*nge 7 haskell parsec

我正在寻找一种简单的方法来组合ParsecT代码的两个部分,它们具有相同的流和monad,但具有不同的用户状态和结果.基本上这样的函数会很好:

withUserState :: u -> ParsecT s u m a -> ParsecT s v m a
Run Code Online (Sandbox Code Playgroud)

问题是,用户状态在某些情况下确实很有用,但我需要在不同的时间使用不同的状态,并且不希望状态类型更大.我是否必须以某种方式修改状态以实现此目的,或者是否已经存在我目前无法找到的功能?

编辑:

我认为替代方案会是这样的

changeUserState :: (u -> v) -> ParsecT s u m a -> ParsecT s v m a
Run Code Online (Sandbox Code Playgroud)

Rom*_*aka 9

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))
Run Code Online (Sandbox Code Playgroud)

请注意,它需要在两者之间正向和反向转换uv.原因是首先需要将环境状态转换为本地状态,运行内部解析器,然后转换回来.

ScopedTypeVariables 和本地类型的签名只是为了清楚 - 如果你愿意,随时删除它们.