ElmとHaskellの6つの非互換性【Elmアドベントカレンダー2014 3日目】

これはElm Advent Calendar 2014の3日目です。

f:id:doloopwhile:20141204015840p:plain

ElmはHaskellベースの言語なので、Haskellの記事や入門書もElmの学習に使えます。しかし、実はElmにはチョコチョコHaskellと違うところがあって互換性はありません。

相違点をElm公式サイトLearnSyntaxFAQから抜粋して解説します。

1. Elmは遅延評価ではありません

Haskellは遅延評価なのでリスト処理の効率が良いのがウリでした。 JavaScriptにもジェネレータという形で遅延評価の思想が取り入れられています。

しかし、Elmのリストや辞書等などは遅延評価ではありません。

Elmはクライアントサイドの言語なので、遅延評価があまり役立たないこと、 SignalJavaScriptのジェネレータのような機能を持っているためのようです。

2. Elmにモナドはありません

Signalがあるので、IOモナドは不要です。それどころかMaybeモナドもListモナドもありません。したがって、do文もありません。

なお、Maybeは標準ライブラリにMaybeパッケージがあり、JavaScriptnullを使うようなコードを安全に書けます。

でも、やっぱりdo文はあった方がいいと私は思うことがあります・・・。caseを5段階ぐらいネストしたりすると。

まぁ「モナド圏論に基づいています*1」という説明が「Haskellってモナドが難しいんでしょ?」という誤解を生むことがないからいいのかな?

3. 関数関連の演算子が違う

Elm Haskell 意味 同値なJavaScript
f x <| g y z f x $ g y z (f x (g y z)) f(x, g(y, z))
g y z |> f x なし
(f << g) x (f . g) x (f (g x)) f(g(x))
(g >> f) x なし

$.がそれぞれ<|<<に変わっています。そして引数の順番を逆にした>|>>が加わっています。

4. :::の意味が逆

Elm Haskell
norm : Float -> Float -> Float
norm x y = sqrt (x * x + y * y)
norm :: Float -> Float -> Float
norm x y = sqrt (x * x + y * y)
1 :: 2 :: alist == [1, 2] ++ alist
1 : 2 : alist == [1, 2] ++ alist

これは型アノテーションの方がリストへの追加よりも頻繁に使うからです。

5. foldl系関数の引数の順序が逆

List.foldr, List.foldl はリストの要素に次々に関数を適用する関数です。 データ構造毎にDict.foldlSet.foldlといった変種もあります。他言語ではreduceinjectという名前で提供されていることがあります。

// elm
v = List.foldr (+) 0 [1,2,3]

// 同等なJS
var list = [1, 2, 3];
var v = 0;
for (var i = 0; i < list.length; i++) {
  v = v + list[i];
}

微妙な違いですがElmではList.foldlが受け取る関数の引数の順序をあえてHaskellと逆にしてあります。

Elm Haskell
List.foldl : (a -> b -> b) -> b -> [a] -> b List.foldl : (b -> a -> b) -> b -> [a] -> b
List.foldr : (a -> b -> b) -> b -> [a] -> b List.foldr : (a -> b -> b) -> b -> [a] -> b

これは、Elmでは標準ライブラリを「データ構造はいつも最後の引数」というルールで設計しているのに関係しています。

たしかに、HaskellのようにList.foldl : (b -> a -> b) -> b -> [a] -> bであったとすると、foldrにはそのまま使えるのにfoldlには使えない関数が出てきて困る気がしますが・・・私にはよく分かりませんでした。

公式サイトのLibrary Design Guidelinesにその点が書いてあるのですが、例がおかしい(間違っている?古い?)気がします。

6. 他にも色々なものがありません。

  • do文以外のモナド関連の構文や演算子
  • where文。代わりにletを使う
  • (+1) のような記法。代わりに((+) 1)と書く
  • ライブラリ

こんなに違って大丈夫?

Haskellは元もと演算子が多い(多すぎる?)言語なので、ElmではSignalが入って互換性がなくなるなら演算子も整理したいと言うことなのでしょう。

ただ「HaskellのライブラリがElmに流用できない」という懸念をもつ方もいらっしゃると思います。私も不安です。

しかし、Haskellは主にサーバーサイドやコンパイラなどに使われ、Elmはクライアントサイドのための言語なので、「ブラウザ上で動くコンパイラを作りたい」というのでもなければ、HaskellのライブラリをElmに流用する機会は実は無いのかもしれません。

あとがき

更新が24時を過ぎてしまいました。

Elm Advent Calendar 2014にはまだ空きがあります。ふるってご参加ください。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

*1:確かにモナドは圏論にルーツがありますが、 「『変数』はデカルトにルーツがあります」「正規表現はオートマトン理論に基づいています」「while文を使用することで原始計算可能な関数だけでなく真に計算可能な関数を作成することが可能になります」ってあえて説明したりしないでしょう?「モナドは圏論(ry」が使われる背景には、数学という虎の威を借りてHaskellを高尚に見せようとする心理があるように私には思えます。