Состояние с 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 }

useless удваивает наш [Int] если он имеет менее десяти элементов и возвращает False, Если он имеет десять или более элементов, он удаляет себя и возвращает True, Сила в том, чтобы иметь X быть рекурсивным в том, что он может выбирать, удалять ли себя после того, как это сделано.

1 ответ

Решение

Проблема заключалась в заказе трансформаторов в стек монады.

Неформально говоря, трансформер не может "отменить" или "аннулировать" эффекты базовой монады, которую он трансформирует. Например, StateT над ExceptT теряет свое состояние при неудаче, в то время как ExceptT над StateT не делает. (Это также причина, почему не может быть IOT трансформер: как обнулить эффект, который уже вырвался в мир?)

Здесь это означает, что внутренний State выживет при любом возврате парсера. Решение состоит в том, чтобы поставить StateT над монадой парсера, не ниже.

Казалось бы, это требует lift вызывает все функции парсера, потому что парсер сейчас не самая внешняя монада. К счастью, liftне нужны, потому что StateT s Parser является примером MonadParsec, который автоматически поднимает все операции парсера.

Другие вопросы по тегам