IPv4アドレスの文字列"192.168.1.1"をパースする方法を考える。IPAddrの内部表現は次のようにする。
|
|
思いつくのは次の2通り。
- ピリオドでsplitして、整数値に変換する。
- パーサを利用する。
いずれにしても結構面倒。この記事では前者だけやる。
準備
適当なディレクトリで次のコマンドを実行。
$ elm init $ elm install elm/parser
src/IPAddr.elm
を作り、内容を以下の通りにする。
|
|
$ elm repl > import IPAddr exposing (..)
方針
次の処理を行う関数をfromString
として定義する。
- 文字列を
.
でsplitする。 - Listの要素数が4でなければ失敗。
- Listの各要素に
String.toInt
を適用し、どれか一つでも失敗すれば全体としても失敗。 - Listを
[a,b,c,d]
としたとき、IPAddr a b c d
を返す。
traverseの実装
3の処理は、次の関数として抽象化できる: リストの各要素にfを適用し、その結果すべてがJust
を返したときだけ、全体の結果を返す。
|
|
原始的な実装
なるべくfoldr
とかを使わずに書こうとするとこんな感じになる。
|
|
case文を使ってネストが深くなってくると、Haskellみたいなパターンマッチが欲しくなってくる。
Maybe.map2を利用した実装
上の実装で、Maybeを2回剥がして値を取り出し、それに対してリストの連結を行なっていることがわかる。実はこの処理を抽象化した関数Maybe.map2
が実装されている。
どちらもJust値だったときのみ、その値を取り出して計算し、Justに包んで返す関数。
> import Maybe > Maybe.map2 (\a b -> a + b) (Just 1) (Just 2) Just 3 : Maybe.Maybe number > Maybe.map2 (\a b -> a + b) Nothing (Just 2) Nothing : Maybe.Maybe number > Maybe.map2 (\a b -> a + b) (Just 1) Nothing Nothing : Maybe.Maybe number > Maybe.map2 (\a b -> a + b) Nothing Nothing Nothing : Maybe.Maybe number
これを利用して書き直すと以下のようになる。
|
|
これはなかなか感動的。
Maybe.map2 + foldrを利用した実装
上の定義を見ているとfoldrでもっと簡単に書けそうだと気づく。なので書く。
|
|
補足
これは実は車輪の再発明で、同じ関数がelm-community/Maybe.Extraで定義されている。
実はElmにもHoogleみたいに、型名から関数を検索するサービスがある。実際、「こんな型の関数ないかな」と思ってElm Searchで検索したら出てきた。
fromStringの実装
ようやく本題に入る。といってもtraverseさえできればあとは簡単。
まずは、文字列を整数値に変換し、さらに[0,255]に収まっているかどうかをチェックする関数parseByte
を定義する。Maybe.andThen
は以下のように、Maybe
値に対してさらに条件をかけて、不正なものを落とすために使われる。
|
|
これを元にfromString
を実装する。
|
|
> IPAddr.fromString "192.168.1.1" Just (IPAddr 192 168 1 1) : Maybe IPAddr.IPAddr > IPAddr.fromString "192.168.1" Nothing : Maybe IPAddr.IPAddr > IPAddr.fromString "192.168.1.1.1" Nothing : Maybe IPAddr.IPAddr > IPAddr.fromString "192.168.1.255" Just (IPAddr 192 168 1 255) : Maybe IPAddr.IPAddr > IPAddr.fromString "192.168.1.256" Nothing : Maybe IPAddr.IPAddr