wvogel日記

自分用の技術備忘録が多めです.

IP adress with Parsec

Parsecを練習しなければ!
ということで


簡単なパーサーとして、
A.B.C.D
のようなIPアドレスから、数値のリスト、
[A,B,C,D]
を生成するパーサーを作ってみた
エラー処理も何もない、単純にドットを消していくだけのパーサです

import Text.ParserCombinators.Parsec

num :: Parser Int
num =  many1 digit
        >>= return.read

ipv4 :: Parser [Int]
ipv4 = do a <- num
          b <- many (char '.'
                      >> num
                      >>= return
                         )
          return (a:b)

main = parseTest ipv4 "255.128.64.10"

エラー処理などは含めていないけれど、
こんな感じで適当に書いたら出来てしまった。
なぜbが、この記述で[Int]になるのか不明.....
ドットを読み飛ばして値を読んで、その値を返すだけだから、
bの型もIntになりそうなのに


というわけで。
ghciですね

> :{
| let { f = do b <- many(
|                        char '.'
|                        >> num
|                        >>= return)
|              return b
|     }
| :}

として、fの型を調べると、やはり[Int]として出てきました。
そしてmanyの型を調べると、

many
  :: Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m [a]

と。
やはり、こいつがリストを生成してくれているんですね。
この関数が成功する間要素を作成したパーサに従い取り出し、リストにしてくれるわけだ。
なるほど!


そして、Applicativeモジュールを使って書きなおす

import Text.Parsec
import Text.Parsec.String
import Control.Applicative hiding (many)

num' :: Parser Int
num' = read <$> many1 digit

ipv4 :: Parser [Int]
ipv4 = (:) <$> num'
               <*> (many $char '.' >> num')

main = parseTest ipv4 "255.128.64.10"

上のものより少しだけ短くなった!!
モジュール宣言が長くなってしまっているので関数部分だけ見ればなんと半分の量!
更に、読みやすくなった.....?笑


今までは
(>>=),(>>)などを使ってdoを消していたけれど、Applicativeを使えばreturnも消せる!!
これからもできるだけdo,returnは使わない形を目指したいと思います。
別に、それが悪いとか思ってるわけではないんですが、命令言語っぽく書けてしまうのはHaskellのスタイルとしてどうなんだろうと疑問に思ったりしています。