Parsec でユーザ用状態を使う
ユーザ用状態
Parsec モナドには、ユーザが状態を使える機能がある。ユーザ用の状態を操作する関数は putState, getState, modifyState の3つだ。それぞれ State モナドの put, get, modify に対応している。Prelude Text.Parsec> :t putState putState :: Monad m => u -> ParsecT s u m () Prelude Text.Parsec> :t getState getState :: Monad m => ParsecT s u m u Prelude Text.Parsec> :t modifyState modifyState :: Monad m => (u -> u) -> ParsecT s u m ()
runP 関数
これらの関数をテストするのには parseTest 関数は使えない。parseTest ではモナドのユーザ状態は使わない設定になっているからだ。そのかわり、runP 関数を使うようになっている。runP 関数の第2引数はユーザ状態の初期値だ。
Prelude Text.Parsec> :t parseTest parseTest :: (Show a, Stream s Data.Functor.Identity.Identity t) => Parsec s () a -> s -> IO () Prelude Text.Parsec> :t runP runP :: Stream s Data.Functor.Identity.Identity t => Parsec s u a -> u -> SourceName -> s -> Either ParseError a
getState の動作を確認してみた。
Prelude Text.Parsec> runP (getState) "hello" "SourceName" "abc" Right "hello"
State モナドのときと同じように状態をスタックとして使ってみる。
Prelude Text.Parsec> pop = getState >>= \xs-> putState (tail xs) >> return (head xs) Prelude Text.Parsec> push x = getState >>= \xs-> putState (x:xs) Prelude Text.Parsec> runP (push "hello" >> pop) [] "SourceName" "abc" Right "hello"
マッチした文字数のカウントもできる。
Prelude Text.Parsec> runP (many (char 'a' >> modifyState (+1)) >> getState) 0 "SourceName" "aaaaa" Right 5
ユーザ状態を実際には何に使うのかは謎だが、Applicative を調べたときにも「何に使うの?」状態だったので、きっと面白い使いみちがあるのだろう。