haskell – 状态与Megaparsec ParsecT没有回溯

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

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

我的想法是,我可以建立一堆我想要对我的状态([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]和[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 }

如果它的元素数少于10个,则无用会使我们的[Int]加倍,并返回False.如果它有十个或更多元素,它将自行删除并返回True. X是递归的力量是它可以选择是否在完成后自行移除.

最佳答案 问题是monad堆栈中变换器的顺序.

非正式地说,变压器不能“取消”或“无效”它变换的基础monad的效果.例如,StateT over ExceptT失败时失去状态,而StateT上的ExceptT则失败. (这也是为什么没有IOT变换器的原因:如何使已经逃逸到世界的效果无效?)

在这里,这意味着内部状态将在解析器的任何回溯中存活.解决方案是将StateT放在解析器monad之上,而不是下面.

这似乎需要对所有解析器函数进行电梯调用,因为解析器现在不是最外层的monad.幸运的是,不需要升降机,因为StateT的Parser是MonadParsec的一个实例,它可以自动解除所有解析器操作.

点赞