Thinking in Erlang

手続き型プログラミング経験者のための関数型
プログラミングガイド

Robert Baruch
autophile@zoominternet.net

Version 0.9.1

February 5, 2007
Copyright
This work is licensed under the Creative Commons Attribution-Share Alike 2.5
License. To view a copy of this license, visit http://creativecommons.org/licenses/bysa/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San
Francisco, California, 94105, USA.

1 導入 ..................................................................................................................5
1.1 この文 書が扱 わない こと .........................................................................5
1.2 この文 書の内 容 ....................................................................................5
1.3 コード の Erlang への 移植 .......................................................................6
1.4 Hello, World......................................................................................6
1.5 hello.erl のコ ンパイ ルと実 行 ..................................................................7

2 変数のスコープ ...............................................................................................9
2.1 状態が ないと いうこ と ...........................................................................9
2.2 状態な しに対 処する ............................................................................11

3 パターンマッチ .............................................................................................12
3.1 パター ンマッ チの基 本 ..........................................................................12
3.2 関数の 引数で のパタ ーンマ ッチ ..............................................................14
3.3 if と case.......................................................................................... 15
3.4 ガード 条件 ........................................................................................17

4 繰り返し ........................................................................................................19
4.1 再帰 ................................................................................................19
4.2 より手 軽なル ープ ...............................................................................22
4.3 手軽な ループ のまと め ..........................................................................25

5 プロセス ........................................................................................................27
5.1 二つの ことを 一度に 行う .......................................................................27
5.2 プロセ ス間通 信 ..................................................................................28
5.3 プロセ スが停 止する 条件 .......................................................................31
5.4 オブジ ェクト として のプロ セス ..............................................................33
5.5 汎用サ ーバ ........................................................................................36
5.6 分散 Erlang......................................................................................36
5.7 同期化 は不要 .....................................................................................37

6 エラー処理 .....................................................................................................38
6.1 「失敗 するに 任せよ 」 ..........................................................................38
6.2 作業プ ロセス と監督 プロセ ス .................................................................39

1 導入

1.1この 文書が 扱わな いこと
もし読者がこの文書からプログラミング自体を学ぼうと期待しているのであれば、きっと
失望させてしまうだろう。読者は C++、C#、Java のいずれかをある程度よく知っていることが
期待されている。同様に、この文書は Erlang のリファレンスではない。Erlang のドキュメントは
すでに多く存在しており、それらと重複するような情報をここに詰め込むことには意味がないか
らだ。
さらに、我々は他の特定の言語(関数型であれ何であれ)の代わりに Erlang を使うとい
う方がよいということを言おうとしているわけでもない。読者は Erlang で何かをしたいと思っ
たからこそこの文書を読んでいるのであり、既に Erlang を使おうと(少なくとも試してみよう
と)決めているのだということを想定している。
最後に、ここには Erlang のインストール方法は書かれていない。繰り返しになるが、特定
の環境に Erlang をインストールするための情報はウェブ上に多く存在している。

1.2この 文書の 内容
Erlang は関数型プログラミング言語である。これは C++、C#、Java のような手続き型プ

ログラミング言語とは非常に異なったものだ。手続き型言語が力点を置いているのはデータの構
造だとか何らかのグローバルな状態を操作する一連の命令とかいったものだ。これに対して関数
型言語はプログラムというものをグローバルな状態を持たない 関数を評価することだという風

に捉える。

熟練した手続き型ソフトウェアのアーキテクトは関数型プログラミング言語にグローバ
ルな状態がないのをみて奇妙に感じるかもしれない。この文書は Java から Erlang へと概念を
結びつけることにより理解を容易にしようとしている。筆者は Java のベテランだが、長い間
C++を放棄し、C#についてはこれまで移行する必要に駆られなかった。そういうわけでアナロ
ジーはほとんどの場合 Java からのものである。

1.3コー ドの Erlang への 移植
手続き型言語から関数型言語へとコードを移植するのは容易なことではない。そのために
はそのコードが何を成し遂げようとしているのかを理解する必要がある。両者のパラダイムが異
なるため、一から新しいコードをデザインしなおさなければならなくなってしまうだろう。これ
こそが Erlang でどう考えるかを知ることが重要な理由なのである。

1.4Hello, World
次にこれがくるということは予想していただろうから、まずはこれから片付けよう。
1 -module(hello).
2 -export([hello/0]).
3
4 hello() ->
5

io:fwrite("Hello, World!~n", []).
図 1 Hello, World

とても変てこに見えるが、これを既に知っているものと結びつけてみよう。1行目はモジ
ュールの宣言で、これはクラスの宣言のようなものだ。これはこのファイル(あるいはモジュー

ル)が hello という名前であるということを言っている。C++や Java とは違ってモジュール名
は小文字で始まらなければいけないことに注意してほしい。これは大文字で始まる語は Erlang
では変数にしか使わないことになっているからだ。

2行目はどの関数がモジュールの外から呼ばれてもいいのかを示している。ところで関数
というのは Erlang ではメソッドのことをそう呼ぶのだ。つまり export によって public メソッ
ドと private メソッドの区別のようなことができる。Erlang には継承がないので protected メ
ソッドに相当するものはない。
export ステートメントの中の大括弧はリストを示している。Erlang のリストは配列のよ
うなものだが、伸縮させたり要素を挿入したり削除したりもできる。後でもっと複雑な例が出て
くるが、今のところはこのリストは1つの要素のみを持っているのだという認識に留めておいて
ほしい。
hello/0 は名前が hello でアリティが 0 であるような関数をエクスポートするということ
を示している。アリティとは単に関数が取る引数の数のことだ。同名でアリティの異なる関数が
2 つあっても構わないが、同名でアリティも同じであるような関数が 2 つある場合は実のところ
同じ関数だ。これについても後で見ていく。
4 行目は関数の宣言だ。この矢印は関数の本体の開始を指し示していて、本体はピリオド
で終わる。
5 行目がその本体だ。ここでは io モジュールの fwrite 関数を呼んでいる。io:fwrite は C
でいう printf のようなもので Java(1.5 以上)では PrintStream.printf にあたる。最初の引数が
書式化文字列で、2 つ目の引数が書式化文字列の使用する物のリストだ。ここで空の大括弧はそ
のリストが空であることを示している。
チルダ文字(~ )は C の printf や Java の PrintStream.printf における%文字に相当す
る。~n は環境依存の改行を出力するための書式指定だ。といっても現実には行送り( ASCII の
0x0a )だけが出力されるので Windows 環境(恐らく LF や CR の代わりに NEL を使う IBM
OS/390 システムも)は十分にサポートされていない。

1.5hello.erl のコン パイル と実行
では Erlang のインタラクティブシェル上で hello.erl のコンパイルと実行に取り掛かろ
う。

ekmac:~ ek$ erl
Erlang (BEAM) emulator version 5.5.2 [source] [async-threads:0]
[hipe] [kernel-poll:false]
Eshell V5.5.2 (abort with ^G)
1> c("hello.erl").
{ok,hello}
2> hello:hello().
Hello, World!
ok
3> init:stop().
ok
4> ekmac:~ ek$
図 2 hello.erl の実行
最初のコマンドがカレントディレクトリの hello.erl をコンパイルしている。ここには絶
対パスや相対パスを書くこともできる。このコマンドは関数の形式を取っていてピリオドで終わ
るということに注意してほしい。
次に hello モジュールの中の hello/0 関数を呼んでいる。これはクラスのスタティックメ
ソッドを呼ぶのによく似ている。ここでも呼び出しはピリオドで終わる。最後の”ok”は単に
hello/0 の戻り値だ。関数は最後の文の戻り値を返す。この場合それは io:fwrite/2 の戻り値で、
この関数は ok という値を返す。[訳注:この ok はアトムという型の値である。3.3 を参照]
最後にシェルを抜けるために init:stop/0 関数を呼び出している。

2 変数の スコ ープ

2.1状態 がない という こと
先ほど簡単に述べたように、Erlang にはグローバルな状態というものがない。これはグロ
ーバル変数がないということを意味している。モジュールはクラスのようなものだとも述べた
が、クラスが属性を持つことができるのに対してモジュールは変数を持つことができない。
Erlang の変数のスコープはそれが宣言された関数の中に限定される。さらに変数は一度
定義されたら再度代入を行うことはできない。これが意味するところは、関数は状態を持ってい
て、なおかつ持っていないということだ。
最後に、Erlang の変数は特定の型を持つものとして宣言することはできない。型は実行時
に決定される。これはある型を他の型と混同することはありえず、同一スコープで変数を再度型
付けするようなこともありえないためだ。以下が変数の用例だ。

Eshell V5.5.2 (abort with ^G)
1> X = 1.
1
2> X.
1
3> X = 2.
=ERROR REPORT==== 17-Jan-2007::19:58:46 ===
Error in process <0.30.0> with exit value:
{{badmatch,2},[{erl_eval,expr,3}]}
** exited: {{badmatch,2},[{erl_eval,expr,3}]} **
4> F = 1.0.
1.00000
5> S = "abc".
"abc"
6> L = [1, 2, 3].
[1,2,3]
7> L2 = [$a, $b, $c].
"abc"
図 3 様々な変数

変数が値を割り当てられたとき、それは束縛されたという風に言う。一度変数が束縛され
ると再度束縛されることはできない。某アメリカ大統領風にいうと「私を一度束縛したものは…
ええと…やるじゃないか。束縛してみろ…二度と束縛されないぞ!」ということだ。[訳注:諺を間
違えていったジョージ・ブッシュのスピーチに由来]
2 行目は変数 X の値を読み出している。3 行目では同一スコープの変数を再束縛している
ためにエラーになっている。このエラーは badmatchというものだが、これについては後ほど説
明する。
4 行目は浮動小数点数、5 行目は文字列、6 行目はリスト、7 行目は文字列のリストを示し
ている。($x は C や Java での単一引用符で括る ’x’ に相当し、$\n が改行文字、$$が$記号に
なる)
L2 が文字列を表示していることに注意してほしい。本当は文字列型というものは Erlang

には存在しないのだ。文字列は文字のリストとして実装されている。実を言うと文字型というも
のも存在せず、文字列は単なる整数のリストである。そうすると 16 ビット整数のリストを作っ
てそれを Unicode 文字列と呼ぶこともできそうだが、Erlang は Unicode をサポートしていな
い。Erlang で Unicode を扱うことについてはこの文書のスコープの範囲外だ。Unicode 文字列
を表現すること自体は簡単なことだが、2 つの Unicode 文字列を適切に正規化して比較するこ
とや、ある Unicode 文字が空白文字であるかどうかを判別することさえ実装されていないとい
うことを述べておけば十分だろう。

2.2状態 なしに 対処す る
ここまでの話で頭がパンクしてしまったかもしれない。読者の C プログラムや Java プロ
グラムでは普通 i++を少なくとも 1 回は使っているに違いない。しかしそれは変数の値を変更
するということになるので Erlang では使うことはできないのだ。
ここで Erlang 流に考える(あるいは関数型風に考える)ということが出てくる。i++は
さておいて、S/=1000 を見てみよう。おそらく S はミリ秒単位で、それを秒単位に変換したいの
かもしれない。我々は Erlang を使っているのでこれは S2=S/1000 のように実装しなければな
らないだろう。

ここで暴動が巻き起こる。
「メモリ使用量を倍にしているだけじゃないか!」誰かが喚く 。
「何てもったいない!」別の者が叫ぶ。人々はパトカーをひっくり返し「Erlang は使えない!」と
合唱する。
さて、どうして S2 を割り当てた後でも S を保持しておくなんて言うのだろうか。S と S2
と両方必要だから? もしそうなら両方保持していく以外に選択の余地はない。もし S が必要
でなければ S2 を割り当てた後に捨ててしまっても構わない。もしくはもし S2 が一度しか必要
でないなら S2 さえ取り去って必要なところで一時変数を使うようにしてもいいだろう。さらに
ひとたび S が定義された関数が終了したなら最早 S も必要ではない。

ポイントは再代入を除去することによってコンパイラが最適化処理を簡単に行うことが
できるようになるということなのだ。
勿論 Erlang シェルでこれをやった場合は、シェルは次にユーザが何を行うか知らないの
で、S を保持しておくという選択しかない。

3 パター ンマ ッチ

3.1パタ ーンマ ッチの 基本
3 要素のタプルを考えよう。タプルというのはリストのようなもので、リストとは違って
通常伸縮せず、リストのような tail(これについては後で述べる)を持たない。Erlang ではタプ
ルを中括弧で表し、{ 1, 2, "burger" }だと 3 要素のタプルとなる。最初の 2 つが整数で、3 つ目
がリストだ[訳注:文字列は整数のリストなので]。
さてこのタプルが middle/1 という関数に渡されたとしよう。この関数はこのタプルを変
数にとるので、middle(Arg)となる。関数は 2 番目の要素を返すようにしたい。
方法としては erlang:element/2 を使うこともできる。これはどんな大きさのタプルに
ついても使うことができて N 番目の要素を返すものだ。しかし、もしタプルが 3 要素から成るこ
とがわかっているのであればもっと簡単な方法がある。
1 middle(Arg) ->
2

{ _First, Second, _Third} = Arg,

3

Second.
図 4 タプルのマッチング
2 行目の代入がパターンマッチだ。まず最初にもし Arg が 3 要素のタプルでなかった場合

はパターンマッチが badmatch エラーで失敗し、この関数は失敗する。しかし我々は middle/1
には 3 項のタプルしか渡さないつもりなのでこれは OK だ(そのつもりじゃないという場合は
後のほうを読んでほしい)。

2 番目に、タプルの各要素は左辺に対してマッチングされ、かつ束縛される。アンダースコ
アで始まる変数も束縛されるが、これはその値が当該スコープで実際に使用されることがないこ
とを暗に示している。もっと簡単に{ _, Second, _}と書くこともできる。これは束縛されるこ
とのない特殊変数 _ を使っているが、文脈によっては先ほどの書き方より意味が明確ではない
かもしれない。
したがって、{ 1, 2, "burger" }が与えられた場合パターンマッチは Second に 2 を割り
当て、他の要素については無視する。そして関数は Second を返す。
先に述べたように、もし middle/1 がタプル以外のものや 3 要素でないタプルを渡された
場合、middle/1 は badmatch エラーで失敗する。
2> test:middle( { 1, 2, "burger" } ).
2
3> test:middle( "burger" ).
=ERROR REPORT==== 18-Jan-2007::19:08:39 ===
Error in process <0.30.0> with exit value: {{badmatch,"burger"},
[{test,middle,1},{shell,exprs,6},{shell,eval_loop,3}]}
** exited: {{badmatch,"burger"},
[{test,middle,1},{shell,exprs,6},{shell,eval_loop,3}]} **
図 5 マッチングの失敗
badmatch エラー情報はユーザが” burger”を何かに対してマッチングさせようとして
test:midlle/1 ( こ れ は shell:exprs/6 か ら 呼 ば れ て い て 、 shell:exprs/6 は さ ら に
shell:eval_loop/3 から呼ばれている)で失敗したということを伝えている。
任意の大きさのタプル内の 2 番目の要素にマッチングさせるには erlang:element/2 を
使うしかないのだが、リストであればどんな大きさのものでもマッチングさせることができる。
以下がリストのパターンマッチングの例だ。

1 middle( [ _First, Second |_Tail] ) ->
2

Second.
図 6 リストのマッチング
1 行目の構文は引数が 2 つの要素を持っていてその後にリストの残りの部分が続くとい

うことを示している。リストの残りの部分というのはリストのことなので、 [ Head | Tail ] を
[ 1, 2, 3, 4, 5 ]にマッチングさせると Head に 1、Tail に[ 2, 3, 4, 5 ]がセットされる。もしリ
ストが tail を持たない場合 tail には空リストがセットされる。これはつまり [ Head | Tail ] を
[ 1 ]にマッチングさせると Head に 1、Tail に[ ]がセットされるということだ。[ Head | Tail ]を
[]にマッチングさせようとすると失敗する。これは Head が要素を必要とするのにリスト中に要
素が存在しないためだ。
ここで 1 行目は少なくとも 2 要素のリストを要求しているということがとわかるだろ
う。もしリストが 2 要素よりも小さかった場合、 _First と Second はマッチングできず、
badmatch エラーで終わることとなる。

3.2関数 の引数 でのパ ターン マッチ
Erlang は実行すべき関数を決定しようとするときに関数の名前だけでなく呼び出し元が
与えた引数にもマッチするものを探そうとする。次に挙げる例はタプルのマッチングを引数のパ
ターンマッチを使ってよりコンパクトなやり方で表現している。
1 middle( { _First, Second, _Third} ) ->
2

Second.
図 7 関数の引数におけるマッチング
最後の 2 つの例(タプルの 2 番目を抽出するものとリストの 2 番目を抽出するもの)

を纏め上げて同じ関数の複数の候補として定義することもできる。

1 middle( { _First, Second, _Third} ) ->
2

Second;

3 middle( [ _First, Second, |_Tail ] ) ->
4

Second.
図 8 関数の候補とマッチング
1 行目と 3 行目で 2 つの middle/1 関数を定義している。個々の関数呼び出しについて

Erlang は当該関数の候補を走査し、その候補を引数とマッチさせようとする。ここでは 2 つの候
補が存在するのが分かる。最初のものは引数が 3 要素のタプルであった場合のみマッチする。2
番目のものは 2 要素以上のリストについてマッチする。
候補は適切な順序で並べておくことが重要だ。 Erlang は最初にマッチするものが見つか
ったところで検索を止めるので、もっとも条件が限定的なものからもっとも条件の緩いものへと
並ぶようにしよう。

最初の候補がピリオドでなくセミコロンで終わっていることに注意しよう。これはコンパ
イラに別の候補が次に来るということを教えてやるためのものだ。2 行目にピリオドを使った場
合、コンパイラは 3 行目が middle/1 の再定義であるとみなしてエラーになってしまう。

3.3if と case
C や Java と同様に if 文と switch 文も存在する。Erlang ではこれらは if と case と呼ば
れる。if は真となるような選択肢の本体、case はマッチした選択肢の本体を実行してその結果の
値を返す。以下がその例だ。

1 is_even(X) ->
2
3

if
X rem 2 == 0 ->

4
5

true;
true ->

6
7

false
end.
図 9 if の例
if 文の中には好きなだけ節を書いてよい。Erlang は各節をチェックし、真と評価された最

初のものを評価して返す。if 文は値を返す文であるため、その結果を変数に割り当てて関数の後
の方で利用するというようなこともできる(i.e. Ret = if...end.)。
case 文はパターンに対してマッチする。
1 many(X) ->
2

case X of

3

[ ] ->

4

none;

5

[ _One ] ->

6

one;

7

[ _One, _Two ] ->

8
9

two;
[ _One, _Two, _Three |_Tail ] ->

10
11

many
end.
図 10 case の例
ここでは X をパターンに対してマッチングしている。マッチした最初のパターンの本体

が評価されて返される。ここでの戻り値は二重引用符で括られていないので文字列ではない。ま
た大文字で始まってもいないので変数でもない。これはアトムといって、大域的な名前空間を持
つ列挙値である [ 訳注 :Lisp や Ruby のユーザはシンボルに相当すると思えばいいかもしれな
い]。アトムはどこでも作ることができ、受け渡ししたりマッチさせたりすることができる。アト

ムをその名前文字列に変換するには erlang:atom_to_list/1 を使う。

多くの場合アトムは文字列よりも効率的であり、また整数を使うよりも意図が分かりやす
い。だから文字列や整数を列挙値として使おうとするような場面ではアトムを使えないか検討し
てみてほしい。
アトムは単一引用符を使って書くこともできる(none の代わりに'none’)。これにより
大文字で始まるアトムや’.’のような記号のアトムを作ることもできる。この場合アトムは”.”
という名前である。まあこの辺は余談だが。
もし 9 行目が何故[ _One, _Two | _Tail ] に対するマッチではないのかと疑問に思ったら
前のセクションの終わりにあるリストの tail のマッチングルールを再読してほしい。
If と case の構文を理解することは重要である。本体の後に別の候補が続く場合、その本
体はセミコロンで終わらなければならない。そして最後のものは必ず end トークンで終わる。も
し if ないし case の後に別のステートメントが連続する場合は end の後にコンマを続けなけれ
ばならない。もし if ないし case が関数内で最後の文である場合はピリオド(その関数の別の候
補が後に続く場合はセミコロン)で終わらなければならない。

つまり一般化して言うとセミコロンは別の候補が次にくるということを示していて、ピリ
オド(if や case の場合は end)はこれで終わりだということを示している。これを覚えておけ
ば Erlang 流に考えられるようになるだろう。

3.4ガー ド条件
関数や case 文はそれらが実行されるべきかどうかについて追加のチェックを行うこと
ができる。これはガードシーケンスと呼ばれるもので、関数を呼んでいいかどうかの追加条件付
きの真偽式である(ただし Erlang マニュアルに記載のあるごく少数の関数には適用不能)。
一例として、ガード条件を使って is_even 関数をこう書きなおすことができる。

1 is_even(X) when X rem 2 == 0 ->
2

true;

3 is_even(_X) ->
4

false.
図 11 ガードシーケンスの例

ガードシーケンスはパターンマッチングと組み合わせて使うと一層便利なものになる。あ
るパターンが最低 1 要素のリストにマッチするが、その要素が偶数かどうかによってはマッチ
できないとしよう。パターンと一緒にガード条件を使うことによってこうしたことが可能になる
のである。

ガードシーケンスはとても複雑なものにすることもできる。上記のようなガードシーケン
スは単一のガード式から成っているが、一般にガードシーケンスは一連のガード条件をセミコロ
ンで区切って並べたもので、そのいずれかが真でなければならない。個々のガード条件はガード

式をコンマで区切って並べたものでその全てが真でなければならない。 Erlang のマニュアルは
ガードシーケンスの構文についてもっと詳細に取り扱っている。

4 繰り返 し

4.1再帰
よろしい、i++がないとしたら Erlang ではどうやって繰り返しを書くのだろうか? 短
く答えるとこうだ。再帰を使う。
…また暴動を引き起こしてしまった。

ちょうど前のセクションで再代入がないということが大した問題ではないと述べたのと
同様に、再帰にしてもその再帰が末尾再帰である限りは問題ないのだ。

末尾再帰とは関数が自分自身を呼び出していて、かつその呼び出しが最後の処理となって
いるようなもののことを意味する。もしある関数の最後の処理が自分自身の(または何であれ他
の関数の)呼び出しである場合、その最後の関数呼び出しに対する引数をセットアップし次第、
当該関数スコープ内で定義された変数(その関数の引数も含む)は除去して構わなくなる。だか
ら末尾再帰を使う場合には関数呼び出しはメモリを追加で使わなくて済むのである。

繰り返しを書きたい場合、繰り返しを行う関数にループ変数を含めなければならない。こ
れは一般に繰り返しを行う専門の関数を定義するのが賢いやり方だということを意味する(何
かをして、ループをして、また何か他のことをやるという関数ではなく)。
次に挙げるのは末尾再帰を使って 1 から 10 までの整数を表示する例である。ここでは引
数のマッチングを使っているが、シーケンシングというまだ紹介していない構文も使っている。

1

-module(count).

2

-export([go/0]).

3
4
5

go() ->
go(1).

6
7
8
9

go(11) ->
io:fwrite("~n", []);
go(X) ->

10

io:fwrite("~.10B ", [X]),

11

go(X+1).
図 12 カウンタの例
このモジュールは go/0 と go/1 という 2 つの関数を定義している。この 2 つはまったく

別の独立した関数だ。両者は互いに何の関係もない。go/0 は単に 1 を引数にして go/1 を呼び出
し、ここがループの開始となる。
10 行目では io:fwrite 文がピリオドではなくコンマで終わっている。文の間にコンマを入
れると文は順次実行される。またここでは関係がないが、順次実行された文のブロックの戻り値
は最後の文の戻り値となる。これは C や Java と同じだ。
この辺で ~.10B が何を意味するかを知るために Erlang のリファレンスに親しんでおく
とよいだろう。ヒント:モジュールのページをチェックして io モジュールを探すこと。あなたが
もっと探しやすい API ドキュメントを作ってくれるなら大歓迎だ。
go/1 の最後の処理が go/1 の再帰呼び出しであるということに注意してほしい。これは
go/1 が適切に末尾再帰を実装しているということを意味している。 11 行目の実行中に X+1 が
セットアップされ、局所変数 X は破棄することができるようになり、go/1 がまた呼び出される。
これは実装上呼び出しの形を取る必要はなく、goto であってもよい。したがって末尾再帰の呼び
出しではスタックが余計に使われることはない。
一方で、もし 10 行目と 11 行目をひっくり返して go(X+1)の後に X の表示がくるように
したとする(したがって数字は逆順に表示される)と、go/1 はもはや末尾再帰ではなくなり、ス
タック上に引数と呼び出し結果を保持しておかなければならなくなるだろう。だから関数は末尾

再帰になるように心がけよう。

以下は非末尾再帰な関数の典型例だ。
1 -module(badFactorial).
2 -export([factorial/1]).
3
4 factorial(0) -> 1.
5 factorial(N) ->
6

N * factorial(N-1).
図 13 よくない再帰
factorial/1 の最後の処理が factorial/1 の呼び出しであるように見えるかも知れないが、

そうではない。実際には factorial(N)は factorial(N-1)が返ってきた後にその結果に N を掛けて
から戻らなければならない。この関数は末尾再帰ではないのでメモリを無駄遣いすることにな
る。

再帰しているのに末尾再帰ではないような関数でも、結果を総計するようなアキュムレー
タを引数に追加することによって「治療」することができる場合がある。以下がアキュムレータを
使って末尾再帰にした階乗の例だ。
1

-module(goodFactorial).

2

-export([factorial/1]).

3
4
5

factorial(N) ->
factorial(N, 1).

6
7
8
9
10

factorial(0, Acc) ->
Acc,
factorial(N, Acc) ->
factorial(N-1, N*Acc).
図 14 適切な末尾再帰
ここには 3 つ関数がある。これらを「セットアップ関数」

「終了関数」

「再帰関数」と呼

ことにしたい。セットアップ関数はエクスポートしているが他の 2 つはプライベートにしてい
る。
この factorial/2 は末尾再帰になっていることが分かるだろう。一度引数を計算したら残
る処理はその引数を使って factorial/2 を呼び出し、何であれその結果を返すということだけだ。
また factorial/2 がその時点までの計算結果を総計するアキュムレータを持ち歩いているという
ことも分かる。N からカウントダウンしていって、その都度アキュムレータ(セットアップ関数
の中で 1 に初期化)に N をかけていき、N が 0 になったところで何もすることがなくなって終
了し(終了関数)、あとは単純にそこまでの総計を返すのだ。

引数としてのアキュムレータの概念は最初のうち理解しにくいかもしれないが、自分でい
くつか関数を書いて試してみると Erlang 流の考え方を習得することができるだろう。
もし N が負の数だったらどうなるだろうか。無限ループに陥ってしまう。これを防ぐには
ガード条件を使うが、これについては後で触れる。

ある種の再帰的な問題には末尾再帰は不適切だったり、末尾再帰にする価値がなかったり
することがある。たとえば左右木のトラバーサルは再帰的に書くことができる。しかし各ノード
について左ノードをトラバースしたら次に右ノードをトラバースするということをしなければ
ならない。左ノードのトラバースがトラバーサルの最後の処理ということにはならないため、こ
のトラバーサルは末尾再帰にすることはできない。

ツリートラバーサルを末尾再帰にすることは可能かもしれないが、そのコードと考え方は
奇妙で追いにくいものになるかもしれない。筆者は奇妙でねじくれたコードからは距離を置くよ
うに薦める。もし目的が必要のないところで効率を出したり、メンテナンスが悪夢になるように
したり、自分がいかに賢いかを証明するためとかだったら話は別だ。だが、そういう人は多分全く
プログラミングをすべきではないだろう。

4.2より 手軽な ループ
リストに対して繰り返しを行うとき、 3 つの効率的なリスト関数を活用することができ
る。その 3 つとは map、foreach、fold だ。これらの関数を使うことでループ関数やアキュムレー
タを作らなくても済むことがある。

map関数は単純に各要素が与えられたリストの対応要素に与えられた関数を適用した結
果であるようなリストを返す。たとえば、3 要素タプルのリストがあったとして、各タプルの 2 番
目の要素を取り出すには以下のようにする。
1 -module(mapping).
2 -export([extract/1]).
3
4 extract(List) ->
5

lists:map(fun extractFromTuple/1, List).

6
7 extractFromTable( {_, Second, _} ) ->
8

Second.
図 15 map の例

26> c("mapping.erl").
{ok,mapping}
27> mapping:extract( [ {1, 2, 3}, {4, 5, 6} ] ).
[2,5]
図 16 map の例の使い方
もし map 関数の引数になる関数が読める程度に短ければ、関数の本体全部を map の呼
び出しに埋め込むこともできる。これは関数を匿名関数にするということで、Java プログラマに
は馴染み深い響きであるはずだ。
1 -module(mapping).
2 -export([extract/1]).
3
4 extract(List) ->
5

lists:map(fun ( { _, Second, _} ) -> Second end, List).
図 17 匿名関数の使用例
foreach関数は map 関数と似ているが、値を返さないという点が異なる。

foldl関数と foldr関数は 1 つの関数と 2 つの追加引数をとる。fold の引数となる関数は要
素とアキュムレータの 2 つの引数を取り、当該要素の値に基づいた新たなアキュムレータの値
を返さなければならない。
foldl と foldr の関数引数ではないほうの 2 つの引数はアキュムレータの初期値と fold の
対象となるリストだ。foldl 関数はリストを最初から最後に向かってトラバースする。これに対し
て foldr は逆方向にリストをトラバースする。foldl は末尾再帰であるため、foldr よりも foldl を
使うほうがよい。
以下は文字列中の a の数を数える例だ。

1

-module(count).

2

-export([count/1]).

3
4
5

count(String) ->
lists:foldl(fun (Element, Acc) ->

6

case Element of

7

$a ->

8

Acc + 1;

9

_->

10

Acc

11

end

12

end,

13

0,

14

String).
図 18 foldl の例

42> count:count("aabdca").
3
Figure 19: Using the example
図 19 foldl の例の使い方

4.3手軽 なルー プのま とめ
リストの各要素に対して(戻り値なしで)何らかのアクションを行う必要がある場合は
lists:foreach を使おう。
リストの各要素について何らかの計算を行う必要がある場合は lists:map を使おう。
リストの各要素の値を累計する必要がある場合は list:foldl か list:foldr を使おう。
lists モジュールには他にもいくつかの手軽なループが提供されている。 lists モジュール

のドキュメントに目を通して何が使えるかということを知っておくとよいだろう。

5 プロセ ス

5.1二つ のこと を一度 に行う
ちょうど Java にスレッドがあるのと同様に Erlang にはプロセスが存在する。しかしグ
ローバルな状態というものが存在しないことから Erlang のスレッドはきわめて軽量なものと
なっている。マニュアルが言うように、 Erlang は大規模な並列処理のために設計されているの
だ。
Java では run メソッドが定義された Thread のサブクラスを作り(あるいは Runnable
インターフェイスを実装したクラスを作り)、その start メソッドを呼ぶことによりスレッドを
開始することができる。Erlang ではそのような必要はない。エクスポートされた関数なら何でも
エントリポイントとして使うことができ、関数が停止したところでプロセスも終了する。

プロセスを開始するには単純に別プロセスで実行したい関数の名前とその関数に渡す引
数を与えて spawnを呼べばよい。spawn 関数は他の関数で利用できるプロセス ID を返す。呼び
出される関数の引数は渡された引数に対してマッチングされる。

1 -module(process1).
2 -export([main/0, thread/0]).
3
4 main() ->
5

Pid = spawn(process1, thread, []),

6

io:fwrite("Spawned new process ~w~n", [Pid]).

7
8 thread() ->
9

io:fwrite("This is a thread.~n", []).

10
図 20 プロセスを spawn する例
9> c("process1").
{ok,process1}
10> process1:main().
Spawned new process <0.65.0>
This is a thread.
ok
図 21 プロセスを spawn した結果

5.2プロ セス間 通信
各プロセスはメールボックスを持っていて、これは受信したメッセージが入る単一のキュ
ーだ。メッセージは何でもよい。文字列でもアトムでも整数でもタプルでもリストでも何でも。プ
ロセスに送るものとして最も便利なのはアトムとアトムが最初の要素に入ったタプルだ。このと
きアトムはメッセージ名で、他の要素は追加のメッセージデータになる。
プロセスは send構文を使って他のプロセスにメッセージを送信することができ、Pid !
Message と書く。Pid が存在しなくても send は成功する。
プロセスは receive構文を使うことによってメッセージを受信するまでブロックするこ

とができる。これはちょうど case のように使える。最初に受信したメッセージに対してマッチ
させる任意の数のパターンをとり、メッセージにマッチした最初のパターンの本体が実行され
る。しかし case とは違って、もしメッセージがマッチしなかった場合はそのメッセージはキュ
ーに再配置され、プロセスはマッチするメッセージを待って再度ブロックする。
この点は重要だ。マッチしなかったメッセージは捨てられることはなく、他の receive 構
文によってマッチされるときまでキューに残り続ける。
receive 構文はオプションでミリセカンド単位のタイムアウト値を取ることもできる。そ
の時間経過後に receive 構文の”after”節が実行される。この意味で after 節は単にマッチされ
るパターンのひとつということになるが、receive 構文の最後に来なければならないという点だ
けが違う。詳細な文法についてはマニュアルを見てほしい。after 節の前にくる本体はセミコロ
ンでは終わらない。

1

-module(receive1).

2

-export([main/0, thread/0]).

3
4

main() ->

5

Pid = spawn(receive1, thread, []),

6

io:fwrite("Spawned new process _w_n", [Pid]),

7

Pid ! hello.

8
9

thread() ->

10

io:fwrite("This is a thread._n", []),

11

process_messages().

12
13 process_messages() ->
14

receive

15

hello ->

16

io:fwrite("Received hello_n"),

17

process_messages()

18

after 2000 ->

19

io:fwrite("Timeout._n")

20

end.

21
図 22 プロセス間通信の例
7> receive1:main().
Spawned new process <0.53.0>
This is a thread.
Received hello
hello
Timeout.
図 23 プロセス間通信の例の実行
process_messages を末尾再帰にしているところに注目してほしい。

この出力で、新規プロセスを生み出し、プロセスが開始し、生み出されたプロセスが hello
メッセージを受け取り、元のプロセスが終了し(send の戻り値は送信されたメッセージなので
出力は hello となる)、最終的に 2 秒後に生み出されたほうのプロセスがタイムアウトしている
のが分かる。

5.3プロ セスが 停止す る条件
ここまででプロセスを通常停止させるには単純にそのプロセスの関数を終了させればよ
いことが分かった。プロセスを即座に停止させるには exitや erlang:errorのような停止関数を
呼び出せばよい。これらは理由コードを引数に取る。理由コードを normal に設定するとプロセ
スを通常停止することができる。
プロセスが normal 以外の理由によって停止した場合、 リンクされた プロセス全てに
exit シグナルが送信される。一方のプロセスが他プロセスの ID で link 関数を呼び出したとき、
その 2 つのプロセスは(双方向に)リンクされていると言い、 unlink 関数を呼んでリンクを解
除することができる。このトピックについて詳しくは「エラー処理」の章を見てほしい。
プロセスはプロセス ID と理由コードを指定して exit 関数を呼ぶことによって他のプロ
セスを停止させることができる。しかしこの場合はそのプロセスが停止したときも exit シグナ
ルは送信されない。

1 -module(receive2).
2 -export([main/0, thread/0]).
3
4 main() ->
5

Pid = spawn(receive2, thread, []),

6

io:fwrite("Spawned new process ~w~n", [Pid]),

7

Pid ! hello,

8

exit(Pid, suckage).

9
10 thread() ->
11

io:fwrite("This is a thread.~n", []),

12

process_messages().

13
14 process_messages() ->
15

receive

16

hello ->

17

io:fwrite("Received hello~n"),

18

process_messages()

19
20
21

after 2000 ->
io:fwrite("Timeout.~n")
end.

22
Figure 24: Example of killing another process
図 24 他のプロセスを kill する例
6> receive2:main().
Spawned new process <0.51.0>
This is a thread.
true
図 25 他のプロセスを kill する例の実行

5.4オブ ジェク トとし てのプ ロセス
プロセスをオブジェクトとみなすことも可能である。もしデータがエントリポイント関数
の引数に保存され、プロセスが受信するメッセージがそのデータに対するオペレーション指示で
あるとしたら、それは事実上オブジェクトだ。以下はオブジェクト内にデータをラッピングする
つまらない例で、コンストラクタ、デストラクタ、set メソッド、get メソッドもついている。

1

-module(object).

2

-export([main/0, new/1, get/1, set/2, delete/1, construct/1]).

3
4

% Testing

5
6

main() ->

7

Object = new(1),

8

io:fwrite("Get data: ~w~n", [object:get(Object)]),

9

set(Object, 2),

10

io:fwrite("Get data: ~w~n", [object:get(Object)]),

11

delete(Object).

12
13 % Interface
14
15 new(Thing) ->
16

spawn(object, construct, [Thing]).

17
18 get(Object) ->
19

Object ! {get, self()},

20

receive

21

{return, Object, Thing} ->

22
23

Thing
end.

24
25 set(Object, Thing) ->
26

Object ! {set, self(), Thing}.

27
28 delete(Object) ->
29

exit(Object).

30
31 % Internals
32
33 construct(Thing) ->
34

io:fwrite("Called constructor: ~w~n", [Thing]),

35

process_flag(trap_exit, true),

36

process_messages(Thing).

37
38 process_messages(Thing) ->
39

receive

40

{get, Caller} ->

41

io:fwrite("Called get~n"),

42

Caller ! {return, self(), Thing},

43

process_messages(Thing);

44

{set, _Caller, NewThing} ->

45

io:fwrite("Called set~n", []),

46

process_messages(NewThing);

47

{’EXIT’, _Caller, _Reason} ->

48

io:fwrite("Called destructor~n", []),

49

true

50

end.

51
図 26 つまらないオブジェクト
2> object:main().
Called constructor: 1
Called get
Get data: 1
Called set
Called get
Get data: 2
** exited: <0.37.0> **
図 27 オブジェクトのお手並み拝見
get 関数の同期的な部分に注意してほしい。他のプロセスからメッセージが来ていたとし
ても処理されるまでそのメッセージ時はキューに残る。実際上は未知のメッセージをフラッシュ
するために何にでもマッチするマッチを receive 節の最後に書いておいて、そこでメッセージ
を破棄する(場合によってはその不正メッセージをログする)のがよいときもあるだろう。

8 行目では正しく get 関数を呼ぶためにモジュール名も明示している。これは大域的な名
前空間に get関数が存在するからだ。この関数は他の大域的な関数と同様に erlang モジュール
のドキュメントに記載されている。
33 行目で引数が Thing ひとつであることに注意しよう。これは Thing ひとつのリストで
プロセスを spawn しているためだ。もしオブジェクトが適切な型の引数で構築されているかも
チェックしたければガード条件を付け加えればよい。
このプログラムでは self関数も使用している。これは現在実行しているプロセスのプロセ
ス ID を取得するもので、Java でいう Thread.currentThread に相当する。
35 行目はプロセスに対して exit シグナルを補足するように指示している。 47 行目でオ
ブジェクトを破棄するために標準の exit メッセージに対するマッチングを行っている。

5.5汎用 サーバ
サーバを実装したプロセスというのは適度に複雑なアプリケーションでは一般的である
ため、Erlang はサーバ作成を容易にするための専用のモジュール gen_server を提供している。
このモジュールはサーバが実装する標準的なインターフェイスや 1 つないし複数のサーバに同
期的または非同期的にメッセージを送るための関数を用意している。さらに詳しい情報はリファ
レンスマニュアルを見てほしい。

5.6分散 Erlang
プロセス ID は別に同一マシン上で走っているプロセスを参照するものでなくてもよい。
Erlang シェルをリモートのマシンで名前(とオプションのホスト名または IP アドレス)付き
で起動すればそのノード名を使ってローカルマシンからそのノードに接続し、ノード名を使って
プロセスを spawn し(これは実際にはリモートマシン上でプロセスを spawn する)、返され
たプロセス ID に対してローカルマシン上のプロセスであるかのようにメッセージを送ることが
できる。詳しくはリファレンスマニュアルを見てほしい。

この分散システムで組み込みのセキュリティは共有クッキーだけだということに注意し

てほしい。ノードのクッキーを知らなければそのノードとは通信を行うことができない。トラン
スポート層でのセキュリティは自動的には提供されないため、重要な情報を送受信しようとする
場合には通信を SSL トンネルか他の暗号/ 認証メカニズム(たとえば Erlang の ssh ライブラ
リ)でラッピングする必要があるだろう。
SSL トンネルを使用する場合、システムはローカルホスト以外からくる接続を遮断する
ように設定しなければならない。このアドバイスは企業で義務付けられるようなセキュリティメ
カニズムについての筆者の経験のみに基づいている。セキュリティに携わる Erlang プログラマ
は通信一般および Erlang のセキュリティについての最新情報をウェブでチェックすることが
推奨される。

5.7同期 化は不 要
Erlang には何と同期化のようなものが存在しない。またそれは必要でもないのだ。他の言
語で同期化を使う場合とは 2 つのスレッドが同じグローバルなデータ要素を同時に改変するの
を防ぐときだということを考慮しよう。 Erlang ではグローバルなデータというものがないのだ
から明らかにこれが発生することはないのである。

6 エラ ー処 理

6.1「失 敗する に任せ よ」
もしエラーに対する Erlang の哲学をスローガンの形に要約することができるとしたら、
それは「失敗するに任せよ」といったものになるだろう。これが意味するところは、コードを書く
ときに防御的なプログラミングはするなということだ。関数引数のマッチングの話に戻ると、も
し関数が整数の処理のみに使われるということを確証したいのであれば引数が整数であること
をチェックするガード条件を関数に付ければよい。ただし非整数のケースをハンドリングするよ
うな追加の節は書かないことだ。
1

% これはよい

2
3
4

good(X) when is_integer(X) ->
X * 2.

5
6

% これはだめ

7
8
9

bad(X) when is_integer(X) ->
X * 2;

10 bad(X) ->
11

{error, "Argument to bad must be integer"}.
図 28 パターンマッチ中でのエラーハンドリングの良い例と悪い例

さて、これらの関数に非整数を渡したら何が起こるだろうか。よいほうの関数は

{badmatch,V}という終了理由で終わる。ここで V は不正マッチを引き起こした値だ。他にもス
タックトレースが表示されてどのようにエラーが生じたかがわかるようになるだろう。これは
Java の関数に不正な引数を渡したときに起こることと一緒だ。 InvalidArgumentaException
が投げられるが、普通こうした例外をキャッチしたりはしない。

だめなほうの場合、関数は「正常に戻る」。そしてプロセスの停止は引き起こされず、返さ れ
たタプルを使って何をするか決めるのは呼び出し元の関数に委ねられている。これは誰にとって
も意味のない余計なコーディングだ。
関数がユーザからの入力(たとえば GUI からの)を引数として取る場合は何が起こるだ
ろうか。その場合入力を検証するのは関数の側ではなく GUI ハンドラに任されている。同じ理屈
が関数が他のプロセスから引数を得る場合にも当てはまる。実際いずれにせよ GUI ハンドラは
別のプロセスということになるだろう。

6.2作業 プロセ スと監 督プロ セス
プロセスは軽量なので機能をプロセスとして分割することを躊躇しないでほしい。もしそ
のプロセスの寿命が短いとしても、だ。何か物事がうまくいかなかったときはプロセスが異常停
止し、そのエラーのハンドリングはリンクされたプロセスに委ねられる。もしリンクされたプロ
セスがそのエラーをハンドリングできない場合は、リンクされたプロセスのほうも異常停止して
さらに上位レベルのプロセスが問題をハンドリングできるようにすべきだ。
これは Erlang で監督ツリー(supervision tree)として知られる考え方に通じる。これは
実際に作業を行う「作業プロセス」と、作業を行わずに作業プロセスを監視する 監督プロセス
(supervisor)が存在するという考え方だ。

例を見てみよう。次にあげるプログラムでは接続を受け入れ、その後で各接続を作業プロ
セスに手渡すようなサーバソケットをセットアップしようとしている。

1

-module(serverexample).

2

-export([start_server/0, handle_connection/1]).

3
4

start_server() ->

5

{ok, ListenSocket} = gen_tcp:listen(8080, [binary]),

6

accept(ListenSocket).

7
8

accept(ListenSocket) ->

9

{ok, Socket} = get_tcp:accept(ListenSocket),

10

spawn(server_example, handle_connection, [Socket]),

11

accept(ListenSocket).

12
13 handle_connection(Socket) ->
14

% do stuff

15
図 29 お手軽サーバ
サーバを開始するために gen_tcp:listen を使い、それを ok に対してマッチングさせてい
る。もしエラーが起こった場合プロセスは異常停止する。その結果、このエラーのハンドリングは
何らかの上位レベルのプロセスに委ねられる。それはサーバが生きているかどうかを(たとえば
死んでいた場合は再起動することによって)保証する監督プロセスということになるだろう。
(図 30 を参照)

こういうやり方で監督ツリーを構築することの一つの利点は、今述べた監督プロセスがより上位
のレベルの監督プロセスによって停止させられた場合、停止する前に子プロセスを停止させると
いうことだ。これにより自動的に安全なシャットダウンが行われる。

図 30 監督プロセスと子プロセス

サーバソケットを開くのに失敗したのでなく、新規接続を受け入れるのに失敗した
(get_tcp:accept が ok にマッチしなかった)としよう。この場合は監督プロセスがサーバを
再起動するようにしたいが、既に接続を処理中かもしれない子プロセスはどうなるだろうか。

接続を処理することだけを気にかけるのであれば問題はない。というのは、子プロセスは
親プロセスとリンクされていない限りは親が停止しても停止することはなく、上記の例ではプロ
セスをリンクしていないからだ(リンクする場合は spawn_link を使う)。しかし、サーバプロ
セスがいくつの接続が現在処理されているのかをトラッキングして、新規接続が来たときにカウ
ンタをインクリメントし、現存の接続が停止したときにカウンタをデクリメントするようにした
いと考えてみよう。

機能を独立したプロセスにするのを躊躇すべきではないという哲学を思い出してほしい。
この場合は接続数をトラッキングする専用のプロセスを作るのだ。これは非効率的に聞こえるか
もしれないが、そんなことはない。Erlang のプロセスはとにかく軽量なのだ。カウンタプロセス
にメッセージを渡して接続カウンタを増減させるというやり方に全く悪いところはない。(図
31 を参照)

図 31 カウンタプロセスの追加

これでサーバが死んでもカウンタプロセスは生き続けて仕掛かりの接続の数を正しく維
持するようになる。

接続が停止するときにはカウンタプロセスにそう通知して接続数をデクリメントできる
ようにしてやる必要がある。我々はサーバプロセスを作業プロセスとして定義してあるのでサー
バプロセスに接続プロセスを監視させるのは賢明ではないだろう。そうするとサーバプロセスが
作業プロセスでありかつ監督プロセスであるようなことになってしまうからだ。

正解はおそらく別個に接続監視のための監督プロセスを書くか、サーバを監督するプロセ
スに接続プロセスの方も監視させるかのいずれかだろう。
残念なことにサーバの Erlang 標準の supervisor モジュールにはリンクされたプロセス
が停止したときにユーザ定義関数を呼ぶ機能がない。 Erlang の supervisor の用途はいくつか
の規定の方法のどれかを使ってプロセス停止をハンドリングすることだ。
しかし、我々は 2 つのプロセスをリンクさせることにより停止シグナルが送られること、
また上の方[ 訳注 : プロセスをオブジェクト化する例 ] で見たように process_flag と trap_exit
を使ってこれらをハンドリングできることを知っている。カウンタプロセスがカウンタをデクリ
メントするのにこのシグナルを使ってもいいわけだ。

ここまではサーバプロセスと接続プロセスが異常停止(そして正常停止)するような条

件を Erlang 流の考え方で扱ってきた。しかしまたカウンタプロセスを追加したことでそのカウ
ンタプロセスについても監督プロセスが必要になってしまう。カウンタプロセスが異常停止する
とどうなるのだろうか。

この場合保持されていた接続数は当然失われてしまう。沢山ある戦略のうちどれを使って
もよいが、最もシンプルな方法は既存の接続をすべて停止してゼロからやり直すことだろう。こ
れは自動的に起こる。というのはプロセス間のリンクは双方向だからだ。ハンドリングできない
exit シグナルを受信するとプロセスは終了する。

これが意味するところは、もしカウンタプロセスが停止し、かつそれが既存の接続プロセ
スとリンクされている場合には、それら全てのプロセスが同様に停止するということだ。
もっと洗練された戦略としては接続プロセスは exit シグナルを無視するようにし、カウ
ンタプロセスが再起動したときに既存のプロセスをすべて探し出してリンクする、といったもの
が挙げられるだろう。こうしたことができる pg2(プロセスグループ 2)と呼ばれるモジュール
が存在する。

プロセスは名前つきのプロセスグループに自身を追加することができ、他のプロセスはそ
のグループに属するプロセスのリストを照会することができる。グループのメンバが停止した場
合、そのメンバは自動的にプロセスグループから除外される。(図 32 を参照)

図 32 カウンタプロセスの監督と接続プロセスグループ

したがってカウンタプロセスは接続プロセスのグループに照会をかけてプロセスを取得
し、それぞれをリンクすることができる。面白いのはカウンタプロセスがある接続プロセスがグ
ループの中に存在すると判断し、その後リンクしようとする前にその接続プロセスが死んでしま
った場合だ。この場合、カウンタプロセスは exit シグナルを受け取り、カウンタをデクリメント
する。

参考文献
[1] http://www.erlang.org とそこで提供されたドキュメント
[2] http://www.erlang.se とそこで提供されたドキュメント。The Programming Rules and
Conventions は綺麗な Erlang コードを書くためのすばらしいガイドだ。

用語索引
badmatch.............................. .................10

ガード式......................... .........................18

erlang\................................................ ........

ガード条件......................... ......................18

error............................................................31

シーケンシング......................... ................19

exit........................................... ..............31

プロセス......................... .........................27

exit シグナル............................... .............31

メールボックス......................... ................28

foldl........................................ ................24

メッセージ......................... ......................28

foldr.................................................... ....24

モジュール............................ .....................6

foreach................................... ................23

リスト............................ ...........................7

get...................................................... ....36

リンク......................... ............................31

map.............................................. ..........23

手続き型プログラミング言語........................5

receive......................... ..........................28

束縛....................................................... ..10

self................................... ......................36

監督ツリー......................... ......................39

send................................ .......................28

監督プロセス......................... ...................39

spawn.............................................. .......27

関数.................................................... ...5, 7

アリティ............................. .......................7

関数型プログラミング言語...........................5

ガードシーケンス.......................... ............17

図表索引

図 1 HELLO, WORLD.........................................................................................6
図 2 HELLO.ERL の実行 ....................................................................................8
図 3 様々な変数 ................................................................................................10
図 4 タプルのマッチング ................................................................................12
図 5 マッチングの失敗 ....................................................................................13
図 6 リストのマッチング ................................................................................14
図 7 関数の引数におけるマッチング

............................................................14

図 8 関数の候補とマッチング ........................................................................15
図 9 IF の例 .......................................................................................................16
図 10 CASE の例 ..............................................................................................16
図 11 ガードシーケンスの例 ..........................................................................18
図 12 カウンタの例 ..........................................................................................20
図 13 よくない再帰 ..........................................................................................21
図 14 適切な末尾再帰 ......................................................................................21
図 15 MAP の例 ................................................................................................23
図 16 MAP の例の使い方 .................................................................................23
図 17 匿名関数の使用例 ..................................................................................23

図 18 FOLDL の例 ............................................................................................25
図 19 FOLDL の例の使い方 .............................................................................25
図 20 プロセスを SPAWN する例 ...................................................................28
図 21 プロセスを SPAWN した結果 ...............................................................28
図 22 プロセス間通信の例 ..............................................................................30
図 23 プロセス間通信の例の実行 ..................................................................30
図 24 他のプロセスを KILL する例 ................................................................32
図 25 他のプロセスを KILL する例の実行 ....................................................32
図 26 つまらないオブジェクト ......................................................................35
図 27 オブジェクトのお手並み拝見

..............................................................35

図 28 パターンマッチ中でのエラーハンドリングの良い例と悪い例

.......38

図 29 お手軽サーバ ..........................................................................................40
図 30 監督プロセスと子プロセス ..................................................................41
図 31 カウンタプロセスの追加 ......................................................................42
図 32 カウンタプロセスの監督と接続プロセスグループ

...........................44