Parsec で Applicative を使う
Applicative
Text.Parsec では Applicative が使える。例えば数字の文字列をスキャンして Int 型のデータを得るには次のようにする。Prelude Text.Parsec> parseTest ((read :: String -> Int) <$> many digit) "123" 123
また、両端のカッコを取り除いて間の文字列を取り出すには次のようにする。
Prelude Text.Parsec> parseTest (char '(' *> many letter <* char ')') "(hello)" "hello"
モナドから取り出さずにモナド値を処理できる Applicative は、モナドを扱うプログラムでは絶大な効果を発揮するようだ。四則演算のパースをするプログラムを Applicative で書き直したら計算までしてくれた。
Prelude> :l calc.hs [1 of 1] Compiling Calc ( calc.hs, interpreted ) Ok, modules loaded: Calc. *Calc> parseTest expr "(1+2)*(3+4)" 21
Applicative の使用例
ファイル名:calc.hs
module Calc whereimport Text.Parsec
expr :: Parsec String () Int expr = sum <$> ((:) <$> term <*> many (char '+' *> term))
term :: Parsec String () Int term = product <$> ((:) <$> factor <*> many (char '*' *> factor))
factor :: Parsec String () Int factor = (char '(' *> expr <* char ')') <|> ((read :: String -> Int) <$> (many1 digit))
上のプログラムは PEG で記述した下記の文法(Wikipedia より)をほぼ逐語的に書き下したものになっている。パーサを実装するときの Parsec と Applicative の威力が分かる。
Value ← [0-9]+ / '(' Expr ')' Product ← Value (('*' / '/') Value)* Sum ← Product (('+' / '-') Product)* Expr ← Sum
パーサを設計する難しさは、プログラムの実装よりも、文法をどう設計して記述するかの難しさによっているような気がする。PEG で記述された文法は比較的容易に Parsec で実装できるのではないか。