Erlangの入門記事 パターンマッチについて

パターンマッチ

 Erlangを理解するためには、パターンマッチを理解しなければなりません。まずは、JavaScriptのswitchに似ているErlangのcaseからです。

例1

case X of
   123 -> a;
   "abc" -> b;
   [1,2] -> c
 end

Xが123の時にはaを返し、
Xが"abc"の時にはbを返し、
Xがリストの[1,2]であればcになります。
もし、Xがこれら以外であれば例外が発生します。

例2

 case X of
   [1,2,A] -> A;
   [3,A,B] -> A+B
 end

上記の例は、 Xが[1,2,3]なら3,[1,2,5]なら5 というふうに、[1,2]で始まる要素3のリストであればマッチし、3つ目の要素が変数Aにバインドされます。
Xが[3]から始まる要素3のリストであれば、[3,4,5]なら9、[3,5,10]なら15というふうに、2番めの要素がAに、3番目の要素がBにバインドされて処理されます。

Javaには無い特性として、単なる数値や文字列との比較だけでなく、リストやタプルなどの比較や、変数へのバインドができます。

例3

A=5
 case X of
   [1,2,A] -> A;
   [3,A,B] -> A+B
 end

先ほどの例にそっくりですが、予めAに5がバインドされています。この場合は、Xは[1,2,5]の時のみ[1,2,A]にマッチします。同じように[3,A,B]は、[3,5,B]となり、例2ではマッチした[3,4,5]ではマッチしなくなります。

=は代入ではない

JavaScriptでは=で変数に代入できます。JavaScriptでは、A=1;A=2; のように何度でも代入できますが、Erlangでは一度しか代入できません。というか、Erlangでは=は代入ではないのです。

= は、実はパターンマッチで、caseの特殊な形と考えるとわかりやすくなります。

A = B.

は、

case B of
  A -> A
end.

なのです。

ですから、一旦Aが何らかの値にバインドされてしまうと、2回目以降はパターンマッチが成功して値が返されるか、例外が起きるかするのです。

繰り返しますが、=はパターンマッチです。ですからJavaScriptでは見慣れない不思議な代入文が見られます。例えば、

{ok,Bin} = file:read_file("data.bin")

これは、ファイルの読み込みが成功した時のみBinにファイルの内容がバインドされ、失敗した場合はパターンマッチに失敗して例外が発生します。

関数定義もパターンマッチ

Fibonacci数列の計算を例にあげます。

JavaScriptでは、

function fib(n) {
    return n < 2 ? n : fib(n - 2) + fib(n - 1);
}

すごく素直に書けば、

function fib(n) {
    if(n==0) return 0;
    else if(n==1) return 1;
    else return fib(n - 2) + fib(n - 1);
}

のように書けるでしょうか。 これは、fib関数のパラメーターが0の時は0、1の時は1、それ以外であればfib(n - 2) + fib(n - 1)というパターンと考えられます。

Erlangでは

fib(0)->0;
fib(1)->1;
fib(N)->fib(N-2)+fib(N-1).

と書くことができます。関数名とそのパラメーターに対する挙動のパターンの定義が関数定義であると考えることができます。

他にも条件分岐のif、メッセージを受け取るreceiveでも -> がでてきます。-> は、あるパターンに対する挙動を記述するパターンマッチの記述なのです。

Elixir,LFE,Erlogを試してみた

Elixir

 構文がErlangとあまりにもかけ離れているので覚えることが多そうで敬遠していたんだけど、全然そんなことなかった。もちろんElixir専用のライブラリを使えばいろいろいいことがあるんだろうけど、Erlangのライブラリの知識を使って作ることもできる。

 Elixirに謎の,とかdoとかがあるのは全部式にするためとか、ブロックを明確にするため。全部式にしてあるからマクロが簡単に適用できる。この徹底の仕方は非常にLispっぽいと思う。コンパイルして既存のErlangのプログラムと容易に混在できる。

Lisp Flavored Erlang

 これはLispそのまま。ちゃんとコンパイルして、これも既存のErlangのプログラムと混在できる。もちろんElixirとも。

Erlog

 これはPrologなんだが、まだ動かし方がわからない。コンパイルはさずがにできないようだ。

ErlangEOS

 iErlangと、構文をErlangから大幅に変えるErlangEOSの2つの言語に軌道修正した。

 これらにLisp的なマクロを適用できないかといろいろ考えてみたんだけど、Lisp的なマクロを適用するためには、例えば関数定義にはdef とか前につける必要がある。

 ブロックがある部分を明確に指示しなければならない。Erlangでは->の後は必ずブロックになるんだな。今気が付いた。Elixirではdoがブロックを表している。

 Lisp的なマクロを使えるようにいろいろ考えると、Elixirの構文に行き着く。Elixirの構文は必然なのだ。

 いろいろ考えてみたけど、俺が欲しかったのってIOだったんだなって気がしてきた。俺が好きなML系言語、IO言語、あんまりモテなそうだけど、一部には好かれるかもしれない。ま、俺が好きだから作るよ。

 ErlangVMでは.netやJavaより遥かに自由に言語の混在ができている印象がある。Erlang動的言語だからかな。簡単に混在ができるから用途に合わせて好きな言語で作ればいいんじゃないだろうか。どんどんErlangが盛り上がるといいな。

ErlangEOSの方針変更

歌舞伎座.tech#3「Real World Erlang/OTP」で発表してきたのだが、全然、まったく、何の反応もない。 以前にLisp系の言語を作ったときは結構反応があったんだが・・・

Elixirに人気があったのにも驚いた。俺はML系言語が好きなのだが、ML系よりscalaの方が人気があるというのが現実なのだろうな。

Lispの状況とErlangの状況は似ているかもしれない。
普段使っているプログラミング言語とは違うエキゾチックな雰囲気に惹かれてちょっと勉強してみるが、実際に仕事に使うようになる人はほんの数%って感じか。

現在Erlangを使っている人たちは、様々な困難を乗り越えて極めている人たちで、Lispの世界であればフツーにLispでプログラムをかける人たちだと思われる。
俺はフツーにErlangが使えないような人たちがErlangを使えるようにしたいのだが、ErlangにはProlog的な宣言型言語の面が多分にあり、これがErlangのパワーの源の1つであり、だからこのパワーをつぶすような構文ではだめで、しかしそれは初心者向けではない・・

そんなわけで、Erlangの構文をそのままにしてPythonみたいなインデントを採用し、機能を拡張していくiErlang と、Erlangの構文から大幅に変えていくErlangEOSの2つで進んでいくことにした。どーせ今は俺しか使っていないしな。