You can use StateT s Parser
, just be mindful that backtracking in the parser also rolls back the state, so you only get those stateful actions that were invoked on the code path with the successful parse.
{-# LANGUAGE OverloadedStrings #-}
import Data.Attoparsec.ByteString.Char8
import Control.Monad.State
import Control.Applicative
test :: StateT Int Parser ()
test = do
many $ choice [
(modify (+1) *> lift (string "car")),
(modify (+1) *> lift (string "cat"))]
pure ()
parseOnly (runStateT test 0) "catcatcat"
-- Right ((),3)
Also, we can use most of the Attoparsec
combinators out of the box, because they have generic types with Alternative
, MonadPlus
, Applicative
or Monad
constraints, and StateT
defines lift-through instances for these. We can use lift
for the basic Parser
-s.