Text.Parsecのパーサの型

前回 Text.Parsec の記事を書いてから、ひと月以上あいてしまった。なんか途中で書くモチベーションが下がったりもしたんだけど、今日は時間がとれたので書く。前回良くわからなかった、パーサの型についてだ。
ここ↓が参考になった。

 cf. Parsec3 におけるパーサーの型 – k16’s note

このページによると、

  • パーサの厳密な型シグネチャは ParsecT s u m a
    • s は抽象化された入力の型。この抽象化された入力をストリームという
    • u はパース時に好きな状態を格納しておく容器の型
    • m はモナド変換子にとって基盤となるモナド
    • a は出力の型

モナド変換子というのは、ここでは ParsecT のことで、正直なんだかよくわからない。
前回書いたコードにある wave パーサの場合、入力は文字列(String)、出力は少数点数のリスト([Double])で、m がモナドである必要があるから、次のようになる。

wave :: Monad m => ParsecT String u m [Double]

コード全体を載せると:

module Main where

import System.Environment ( getArgs )
import Text.Parsec

--------------------------------------------------------------------------------

main :: IO ()
main = do
  args <- getArgs
  cs <- readFile (head args)
  let wv = parseWave cs
  either (\ e -> print e) (\ w -> putStr $ unlines $ map show w) wv

--------------------------------------------------------------------------------

-- Parsers

wave :: Monad m => ParsecT String u m [Double]
wave = do
  ls <- many1
  waveLine
  eof
  return $ concat ls

waveLine :: Monad m => ParsecT String u m [Double]
waveLine = do
  ws <- many1
  waveData
  newline
  return ws

waveData :: Monad m => ParsecT String u m Double
waveData = do
  d <- count 8 dataChar
  return $ read d

dataChar :: Monad m => ParsecT String u m Char
dataChar = digit <|> oneOf "-. "

--------------------------------------------------------------------------------

parseWave :: String -> Either ParseError [Double]
parseWave input = parse wave "(unknown)" input

--------------------------------------------------------------------------------

実行例:

takatoh@apostrophe $ runhaskell parsewave0.hs data0.dat > result0.txt
takatoh@apostrophe $ head result0.txt
-5.0e-2
-5.0e-2
-5.0e-2
-5.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-7.0e-2

話はさらに続く。
Text.Parsec.String には、次のような定義がある。

type Parsec s u = ParsecT s u Identity
type Parser = Parsec String ()

上は ParsecT のモナドに Identity を使ったもの、下はその Parsec の入力に String、容器に () を使ったものだ。これを使うと、前述の wave パーサの型は次のように簡潔に書ける。

wave :: Parser [Double]

コード全体を載せよう。

module Main where

import System.Environment ( getArgs )
import Text.Parsec
import Text.Parsec.String

--------------------------------------------------------------------------------

main :: IO ()
main = do
  args <- getArgs
  cs <- readFile (head args)
  let wv = parseWave cs
  either (\ e -> print e) (\ w -> putStr $ unlines $ map show w) wv

--------------------------------------------------------------------------------

-- Parsers

wave :: Parser [Double]
wave = do
  ls <- many1
  waveLine
  eof
  return $ concat ls

waveLine :: Parser [Double]
waveLine = do
  ws <- many1
  waveData
  newline
  return ws

waveData :: Parser Double
waveData = do
  d <- count 8 dataChar
  return $ read d

dataChar :: Parser Char
dataChar = digit <|> oneOf "-. "

--------------------------------------------------------------------------------

parseWave :: String -> Either ParseError [Double]
parseWave input = parse wave "(unknown)" input

--------------------------------------------------------------------------------

実行例:

takatoh@apostrophe $ runhaskell parsewave0a.hs data0.dat > result0a.txt
takatoh@apostrophe $ head result0a.txt
-5.0e-2
-5.0e-2
-5.0e-2
-5.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-7.0e-2

ふぅ、今日はここまで。