Elmを始めたばかりの人がリストの操作に対して疑問や機能不足を感じるとの意見がたびたび見受けられるので、カルチャーショックを無くし、Elmをよりよく使っていただけるように記事を残しておきます。
コレクションライブラリのパターン
他の言語でよく見られるパターンとして、基本的に配列を扱い続けるパターンといくつかのコレクションを汎用インターフェースで扱うパターンの二種類があります。(他にもこんなパターンの言語があるよ!というのがあったら教えてください)
一つ目のパターンの言語としては、JavaScript(TypeScript)・Python・Ruby・Go(スライス)等の言語が挙げられます。配列リテラルなどを起点として使われることが多いのが特徴です。他にも配列の機能拡張をサードパーティのライブラリなどが補う使い方もよく見られます。ElmではListがリテラル [1, 2, 3]
によって生成されるのでこちらのパターンに近いですが、CoreパッケージだけでもArray
とSet
の二つのコレクションが存在します。また、サードパーティの補助ライブラリも多く存在します。
二つ目のパターンの言語としては、Java・Scala・C#等の言語が挙げられます。Javaでは配列が存在するものの共変であったり、汎用性の観点などからパフォーマンスがシビアではない場合、基本的にList
インターフェースを実装したArrayList等の利用を推奨されます。Elmでは、そもそもインターフェースの概念が存在しないため、このパターンの考えには属しません。
それでは、ElmのListパッケージの特徴とArray, Setパッケージの特徴を見比べていきましょう。
Elmのコレクションパッケージの特徴
Listパッケージ
ElmのListは、線形リストのデータ構造とその操作を集めたパッケージです。線形リストは関数型のイミュータブルな特徴やパターンマッチとの相性がよく多くの場合は、これで事足りてしまいます。先頭の要素に対する操作や全ての要素に対して何かを計算する場合に適しています。代わりにランダムアクセスや集合的操作をするには適していません。例えば、以下のように添字を指定して要素を取り出すような操作はListパッケージには存在しません。
node
> [1, 2, 3, 4][1]
2
Arrayパッケージ
Arrayは、配列のデータ構造とその操作を集めたパッケージです。配列は添字を利用したランダムアクセスに優れたデータ構造です。他の言語と違う配列の特徴として、Listと同様に一種類の型の値しか含めることができません。またイミュータブルなデータ構造のためsetやpush、appendは新しい配列を生成します。他にもgetは、配列外参照をした場合を考慮して、Maybe型が戻り値となっています。逆に言えば、map, foldl(r), filterのような汎用関数以外はArrayではサポートしていません。代わりに List <-> Array を相互変換する関数を備えているためデータ構造が得意としない操作は新しく自分で作らない限りはそもそもできないようになっています。Setも同様ですが、これがElmのコレクションパッケージの一番キモな特徴になっています。
Setパッケージ
Setは、Set(集合)のデータ構造とその操作を集めたパッケージです。Setは値の被りがないことが保証された、数学の集合の特徴を備えたデータ構造です。挿入や削除、その値が入ってるかどうか(member)が、*O(log n)*で処理できることができます。他にもListやArrayにない集合的な特徴として、集合同士の和(union)や共通部分を取り出す積(intersect)、差(diff)を求めることができます。あとはArrayと同様に汎用関数とListとの相互変換の関数を持ちます。
extraパッケージ
Coreパッケージは本当によく使われる操作だけをシンプルに保つようにしているため、難しいコレクション操作をしたい場合には不足だなと感じてしまうシーンがあります。そういった場合には、[Collection]-extraのような拡張パッケージがElmコミュニティから提供されています。例えば、list-extraなどが存在します。Elmの特徴としてインターフェースやマクロなどが無い分、抽象度が高いライブラリが作れませんが、代わりに文法がシンプルで具象度が高いため関数を手軽にコピーすることでライブラリの依存を減らしたり、関数型の勉強がとてもしやすかったりして、個人的にかなり気に入っています。
まとめ
Elmでは、多くの場合Listが万能に仕事をこなしますが、主に計算量を考慮して効率的な操作では無い場合には、無理に汎用的なコレクションとはせず役割分担を別なコレクションに任せることで、誰でも一定のパフォーマンスが発揮出るようになっています。また、質が高く学習しやすいCoreパッケージ以外のライブラリも豊富なため、他の言語を利用する場合でも役に立つことが多いです。それでは良いElmライフを!