4

我有一个解析器定义为以下稍微复杂的版本:

data X = X { getX :: State ([Int], [X]) Bool }
type Parser = ParsecT Void String (State ([Int], [X]))

这个想法是我可以建立一堆我想对我的状态(the [Int])执行的操作,然后根据情况以任何顺序或我想要的任何时候执行它们:

-- Run the first state in the list.
executeOne :: Parser Bool
executeOne = do
  s@(_, fs) <- get
  let (r, s') = (flip runState s) . getX . head $ fs
  put s'
  return r

例如,执行的操作可能会重新排序操作堆栈或修改[Int].

除了设计决策(我确信有更好的方法可以做到这一点),回溯似乎try不适用于状态。具体来说, ParsecT 的状态将回溯,但内部状态([Int]and [X])不会。为什么是这样?我是在滥用 ParsecT 还是奇怪的递归X业务把一切都搞砸了?我需要Control.Monad.State.Strict改用吗?

编辑:要回答评论者关于示例的问题X,这里有一个:

useless :: X
useless = X $ do
  (vs, xs) <- get
  if length vs >= 10
  then do { put (vs, tail xs) ; return True }
  else do { put (vs ++ vs, xs) ; return False }

useless[Int]如果它的元素少于十个,则加倍我们,并返回False. 如果它确实有十个或更多元素,它会删除自己并返回True。递归的强大X之处在于它可以选择在完成后是否删除自己。

4

1 回答 1

1

问题在于 monad 堆栈中转换器的顺序。

通俗地说,转换器不能“取消”或“取消”它转换的基本单子的效果。例如,StateToverExceptT在失败时会丢失其状态,而ExceptToverStateT不会。(这也是为什么不能有IOT变形金刚的原因:如何消除已经逃到世界上的效果?)

在这里,这意味着内部State将在解析器的任何回溯中幸存下来。解决方案是将解析器放在StateT 上面,而不是放在下面。

这似乎需要lift调用所有解析器函数,因为解析器现在不是最外层的 monad。幸运的是,lift不需要 s ,因为StateT s Parser它是 的一个实例MonadParsec,它会自动解除所有解析器操作。

于 2019-02-13T23:19:11.220 回答