Erlangは、並行処理/分散処理/耐障害性といった特徴を備えた関数型プログラミング言語およびその実行環境だ。
Erlangは、並行処理/分散処理/耐障害性といった特徴を備えた関数型プログラミング言語およびその実行環境のこと。もともとはエリクソンのCSLab(Computer Science Laboratory)で開発されたが、現在はオープンソースソフトウェアとして公開されている。ElixirがErlangで実装されていることでも最近は有名だ。
Erlangの特徴としては以下が挙げられる。
OSネイティブなプロセスよりもはるかに軽量な「プロセス」を持ち、独自の実行環境(Erlang VM)上で動作する。また、Erlangのプロセスは他のプロセスからは分離されていて、メモリ領域を他のプロセスと共有することがなく、プロセス間でのデータ共有はメッセージパッシング機構を使用して行う。このため、Erlangではあるプロセスに問題が発生しても、他のプロセスに影響が及ぶことが少ない。こうした特徴から、Erlangではスケーラブルでリアルタイム性を持った、可用性の高いプログラムを構築できる。
この他にも、ホットスワップ(システム稼働中に、それを停止させることなく、プログラムの入れ替えが可能)、マシン透過なプロセスといった特徴もある。
以下では、Erlangのシェル環境と簡単なスクリプトを見ていく。なお、Erlangのインストールなどの説明については割愛する。
Erlangシェルでは対話的にコマンドを入力したり、作成したスクリプトをコンパイルしたりできる。
まずはシェルを使ってErlangの言語としての特徴を見てみよう。シェルにはコマンドやErlangの式を入力できる。
1> variable = 10.
** exception error: no match of right hand side value 10
2> Variable = 10.
10
3> Variable.
10
4> Variable = 20.
** exception error: no match of right hand side value 20
上の実行例を見ると分かるが、Erlangでは変数の名前は英大文字で始める(1行目でエラーが発生しているように英小文字で始めることはできない)。上のコードで行っているのは代入ではなく、パターンマッチであり、等号の左右の項がマッチするように変数Variableには10が束縛されている。変数への値の束縛(代入)は一度しか行えない(Erlangシェルではf()コマンドで束縛を解除できる)。
なお、Erlangでは任意の型のデータのことを「ターム」(term、項)と呼ぶ。式や関数の「項」だと考えればよい。シェルに入力する式、コマンドはピリオドで終了する点にも注意したい。
次に無名関数を作成してみる。無名関数の宣言にはfunキーワードを使用する。
5> Adder = fun(X) ->
5> fun(Y) -> X + Y end
5> end.
#Fun<erl_eval.6.52032458>
6> Add2 = Adder(2).
#Fun<erl_eval.6.52032458>
7> Add2(5).
7
Erlangでは関数はファーストクラスオブジェクトであり、上のように関数を返す関数も簡単に記述できる。名前を持った関数は以下に示すように、.erlファイルにモジュールを作成して、その中に記述していく。
-module(foo).
-export([getCount/2]).
getCount([], Count) ->
Count;
getCount([H|T], Count) ->
getCount(T, Count + 1).
ここではリストの要素数を取得する関数を定義している。最初の2行でモジュール(foo)とそのモジュールが外部に公開する関数(getCount/2)を記述している。「getCount/2」の「/2」は関数の引数の個数を表すもので、「アリティ」と呼ばれる。
最後の4行がgetCount関数の定義となる。上の例のように、Erlangでは1つの関数に対して、複数の関数ボディーを定義可能だ。ボディーはセミコロン(;)で区切り、最後にはピリオド(.)を記述する(同じ名前で引数の数が異なる関数も定義可能)。
この関数を例えば「getCount([1, 2, 3], 0)」のように呼び出すと、実引数とパラメーターの「パターンマッチ」が行われ、最初にマッチした関数ボディーが実行される。この場合は、getCount関数に空のリスト([])と数値が渡されると最初の関数ボディーが実行され、空でないリストが渡されると2つ目の関数ボディーが実行される。このとき、パラメーターCountにはもう1つの実引数の値が束縛される。
2つ目の関数ボディーには「[H|T]」という記述がある。Erlangでは、リストを「[先頭要素|それ以降の要素]」のように記述できる(「H」は「Head」を、「T」は「Tail」を意味する)。この場合、パラメーターTにはリストの第2要素以降が束縛される(この時点で元のリストから要素が1つ減っている)。そして、要素が1つ減ったリストと1だけ加算されたCountを指定して、自身を再帰的に呼び出している。このようにすることで最終的には1つ目の関数ボディーが実行され、リストの要素数が得られる。
このようなリスト操作はErlangプログラミングにおける基本となる。これを実行するにはErlangシェルでソースファイルをコンパイルする。以下に例を示す。関数呼び出し時にリストの要素数の初期値として0を渡していることにも注意しよう。
8> c('foo').
foo.erl:6: Warning: variable 'H' is unused
{ok,foo}
9> foo:getCount([1, 2, 3, 4, 5], 0).
5
10> foo:getCount(["foo", "bar", "baz"], 0).
3
最後に、Erlangでのプロセスの扱いを簡単に紹介する。
-module(sample).
-export([fact/1, factproc/0]).
fact(0) -> 1;
fact(N) -> N * fact(N-1).
factproc() ->
receive
N when N < 0 ->
io:format("can't compute~n"),
factproc();
N ->
io:format("factorial of ~p is ~p~n", [N, fact(N)]),
factproc()
end.
詳細は割愛するが、fact関数は階乗の計算を実際に行う関数だ。factproc関数はプロセスとして実行され、メッセージとして階乗を求める数を受け取るようになっている(receive式)。receive式にも複数のボディーがある。最初のボディーにはガード節が記述されていて、負数が渡された場合には計算をしないようにしている。また、各ボディーでは最後に「factproc()」として自身を呼び出すことで、プロセスが終了しないようにしている。
これをコンパイル/実行した様子を以下に示す。
11> c('sample').
{ok,sample}
12> Pid = spawn(sample, factproc, []).
<0.79.0>
13> Pid ! 6.
factorial of 6 is 720
6
14> Pid ! -1.
can't compute
-1
上を見ると分かるように、プロセスを新規に開始するにはspawn関数を利用する。また、作成したプロセスにメッセージを送信するには「プロセス ! メッセージ内容」とする。
Erlangは、並行処理/分散処理/耐障害性といった特徴を備えた関数型プログラミング言語およびその実行環境だ。本稿では関数やリストの扱い、プロセスの作成とメッセージ送信といったその基本となる要素を簡単に紹介した。
Copyright© Digital Advantage Corp. All Rights Reserved.