Состояние с 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
, который автоматически поднимает все операции парсера.