Состояние с Megaparsec ParsecT не возвращается - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть парсер, определенный как несколько более сложная версия следующего:

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 Ответ

0 голосов
/ 14 февраля 2019

Проблема заключалась в том, что преобразователи в стеке монад имели порядок.

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

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

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

...