Skip to content

Erlang チートシート

BEAM VM 上で動作するコンカレント指向関数型言語、フォールトトレラントな分散システム向けに設計。

01

基礎

Hello World とモジュール

各 Erlang ファイルは -module(name) で宣言されるモジュールです。エクスポート関数は -export([name/arity]) で列出されます。arity(引数の数)は関数の同一性の一部です——greet/0 と greet/1 は別の関数です。シェルで c(Module) でコンパイルします。

erlang
% hello.erl
-module(hello).
-export([greet/1, greet/0]).

greet() ->
    greet("World").

greet(Name) ->
    io:format("Hello, ~s!~n", [Name]).

% Compile & run in the shell:
%   c(hello).
%   hello:greet("Alice").   %=> Hello, Alice!

変数とアトム

変数は大文字(または _)で始まり、単一代入です——一度束縛されると変更できません。束縛済み変数への再マッチは実際にはアサーションです:X = X+1 は badmatch エラーを発生させます。アトムは小文字で始まる名前付き定数で、グローバルテーブルに格納され同一性で比較されます。

erlang
Name = "Alice".        % variables start with uppercase
Age = 30.               % bound once — single assignment!
% Age = 31.             % ** exception error: no match of RHS

% Atoms start with lowercase (or are quoted)
ok.                    % the ok atom (common return value)
error.                 % another atom
'This is an atom'.     % quoted atom with spaces

true, false, undefined % built-in atoms

% Pattern matching binds and asserts
{ok, Result} = {ok, 42}.   % Result = 42
{error, _} = {error, badarg}.  % _ matches and discards

タプルとレコード

タプルは {a,b,c} と書く固定サイズコンテナです。レコードは -record で宣言される名前付きフィールドを持つタプルです。レコードのフィールド更新は新しいレコードを作成します(不変データ)。最初の要素は慣習的にタグアトム(例: point や person)で、パターンマッチを明確にします。

erlang
% Tuples (fixed-size, positional)
Point = {point, 3, 4}.
{point, X, Y} = Point.      % X=3, Y=4
element(2, Point).          %=> point

% Records (syntactic sugar over tuples)
-record(person, {name, age, city = "Unknown"}).

P = #person{name = "Alice", age = 30}.
P#person.name.              %=> "Alice"
P2 = P#person{age = 31}.    % update field
{person, Name, Age, _} = P. % underlying tuple

リストとリスト内包表記

リストは [Head|Tail] で構築される片方向リストです。++ は連結し(左側 O(n))、-- は要素を削除します。リスト内包表記 [Expr || 修飾子1, 修飾子2, ...] はジェネレータ(X <- List)とフィルタ(ブール式)をサポートします。

erlang
[1,2,3] ++ [4,5].          %=> [1,2,3,4,5]  (append)
[1,2,3] -- [2].            %=> [1,3]        (subtract)
hd([1,2,3]).               %=> 1            (head)
tl([1,2,3]).               %=> [2,3]        (tail)

% Pattern matching on lists
[Head | Tail] = [1,2,3].   % Head=1, Tail=[2,3]
[A,B|Rest] = [1,2,3,4].    % A=1, B=2, Rest=[3,4]

% List comprehensions
[ X*2 || X <- [1,2,3,4] ].          %=> [2,4,6,8]
[ X || X <- [1..10], X rem 2 =:= 0 ]. % evens: [2,4,6,8,10]
[ {A,B} || A <- [1,2], B <- [a,b] ]. % cartesian product

関数とパターンマッチ

関数は ; で区切られ、ピリオドで終了する複数の句として定義されます。句は上から順に試され、最初にパターンとガードが成功したものが使われます。{ok, Val} / {error, Reason} のようなタグ付きタプルが例外なしで成功/失敗を示す標準的な方法です。

erlang
-module(math_tools).
-export([factorial/1, fib/1, classify/1]).

% Multiple clauses selected by pattern matching
factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).

% Fibonacci with guards
fib(0) -> 0;
fib(1) -> 1;
fib(N) when N > 1 -> fib(N-1) + fib(N-2).

% Tagged returns (common Erlang idiom)
classify(X) when X < 0  -> {negative, X};
classify(0)             -> zero;
classify(X) when X > 0  -> {positive, X}.

マップ

マップ(R17 以降)はキー値辞書です。=> で設定/挿入(存在しなければ作成)、:= で更新(キーが既存でなければならない)。パターンマッチでは := のみ許可されます。maps:get/2 はキー不在で badarg を発生させます;maps:get/3 でデフォルト、maps:find/2 で {ok,_}|error タプルを取得。

erlang
% Create
M = #{ name => "Alice", age => 30, role => admin }.

% Lookup
maps:get(name, M).              %=> "Alice"
maps:get(city, M, "Unknown").   % default if missing
{ok, V} = maps:find(age, M).    %=> {ok, 30}

% Update / add (immutable — returns new map)
M2 = M#{ age => 31, city => "Paris" }.

% Pattern match on maps
#{ name := N } = M.            % N = "Alice"  (:= for matching, => for setting)
#{ age := A, role := R } = M.
02

パターンマッチとガード

関数ヘッドでのパターンマッチ

Erlang は引数の構造に上から下へパターンマッチして関数句を選択します。パターンは新しい変数を束縛し、リテラルや既存変数と等価を表明し、_ でフィールドを無視できます。同じ機構が receive、case、= に使われます。

erlang
% Select clause by structure of argument
handle({ping, From}) -> From ! pong;
handle({echo, Msg})  -> Msg;
handle({add, A, B})  -> A + B;
handle(stop)         -> bye.

% Tuple destructuring
-record(rect, {w, h}).
area(#rect{w=W, h=H}) -> W * H.

% List patterns with literal values
greet_first([H|_]) -> io:format("Hi ~p~n", [H]);
greet_first([])    -> ok.

case 式

case は式を評価し一連のパターンとマッチします。各句にガードを持てます。関数の分岐が元の引数ではなく計算値に依存する場合の標準的な制御フローツールです。予期しない入力でクラッシュさせたい場合を除き、_ 全catch 句を含めてください。

erlang
classify_file(Size) ->
    case Size of
        0              -> empty;
        N when N < 1024 -> small;
        N when N < 1048576 -> medium;
        _              -> large
    end.

% Matching on tagged tuples
Result = case file:read_file("a.txt") of
    {ok, Bin} -> {ok, binary_to_list(Bin)};
    {error, enoent} -> {error, missing};
    {error, Reason} -> {error, Reason}
end.

ガード

ガードは句がいつマッチするかを制限します。既知の組込み関数セットに限定されるため、常に終了し副作用がありません。カンマ = AND、セミコロン = OR。ガードは再評価されるため、可能ならパターンマッチを優先してください。

erlang
% Guards go after 'when' and may use built-in guard tests (BIFs)
max(A, B) when A >= B -> A;
max(_, B)             -> B.

% Multiple guard tests: ',' = AND, ';' = OR
classify(N) when N > 0, N < 100 -> in_range;
classify(N) when N =< 0; N >= 100 -> out_of_range.

% Allowed guard BIFs include comparison, arithmetic, type tests:
% is_integer/1, is_atom/1, is_list/1, is_map/1,
% length/1, map_size/1, abs/1, element/2, hd/1, tl/1
% (No user functions — guards must be side-effect free and terminate!)

ビット構文とバイナリ

ビット構文 << Seg1, Seg2, ... >> はビットフィールドでバイナリをパック/アンパックします。各セグメントは Value:Size/TypeSpecifier で、Type は integer|binary|float|bits、オプションで符号とエンディアン。Erlang はネットワークプロトコルやファイルフォーマットの解析に優れています。

erlang
% Pack a 16-bit length + 8-bit type + payload
Bin = << Length:16, Type:8, Payload/binary >>.

% Unpack with the same pattern
<< L:16, T:8, Rest/binary >> = Bin.

% Bit fields & endianness
<< N:32/big-unsigned-integer >> = <<0,0,1,1>>.   % N = 261
<< N:32/little-signed-integer >>  = <<1,0,0,0>>. % N = 1

% IPv4 header parsing example
<< Version:4, IHL:4, _TOS:8, TotalLen:16,
   _/binary >> = Packet.
03

並行プログラミング

spawn — プロセスの開始

spawn(M,F,A) は新しい BEAM プロセスを作成します——独自のヒープとメールボックスを持つ軽量グリーンスレッド。プロセスはメモリを共有せず、メッセージパッシングでのみ通信します。BEAM は数千万のプロセスを実行でき、各プロセスは約 2-3 KB です。

erlang
% spawn/3 starts a new lightweight process
Pid = spawn(Module, Function, ArgsList).
Pid = spawn(fun() -> timer:sleep(1000), io:format("done~n") end).

% Every process has a PID; processes share NO memory
% (BEAM processes are NOT OS threads — millions are possible)

% self() returns the current process's PID
io:format("I am ~p~n", [self()]).

% is_process_alive/1 checks liveness
case is_process_alive(Pid) of
    true  -> ok;
    false -> {error, dead}
end.

send (!) — メッセージパッシング

Pid ! Msg は非同期に受信者のメールボックスへメッセージを送信します。配送は保証され、同じプロセス対間で順序が保たれます。! はメッセージ自体を返すため、ブロードキャストリストが可能です。プロセスはアトム名で登録でき、PID を持たずに送信できます。

erlang
% Send a message: Pid ! Message
Pid ! {hello, self()}.
Pid ! ping.
Pid ! {compute, 1, 2, 3}.

% ! always returns the message itself, so you can broadcast:
[ P ! broadcast || P <- AllPids ].

% Send to a registered process (by atom name)
registered_name ! {request, Ref, self()}.

% Register / unregister a name for a PID
register(server, Pid).
unregister(server).
whereis(server).   %=> Pid | undefined

receive — メッセージのパターンマッチ

receive はメールボックスを順にスキャンし最初のマッチするメッセージを探します;なければ到着まで中断します。after 句でタイムアウトを設定します(0 = 非ブロッキング、infinity = 永久)。一般的なサーバーパターンは各メッセージ後に自身を呼ぶ尾再帰 loop() です。

erlang
% receive blocks until a matching message arrives
loop() ->
    receive
        {ping, From} ->
            From ! pong,
            loop();
        {echo, Msg} ->
            io:format("echo: ~p~n", [Msg]),
            loop();
        stop ->
            ok
    end.

% receive with a timeout
receive
    {data, X} -> process(X)
after 5000 ->
    {error, timeout}
end.

% Flush mailbox (drain all messages)
flush() ->
    receive _ -> flush() after 0 -> ok end.

選択的・非選択的 receive

receive は選択的です:古いメッセージがあっても最初のマッチを探します。一意の参照 Ref で特定の返信をマッチするのに最適ですが、古い非マッチメッセージが蓄積しスキャンが遅くなります。gen_server は全メッセージを順に処理してこれを回避します。

erlang
% Selective receive: skips non-matching messages
get_reply(Ref) ->
    receive
        {Ref, Reply} -> Reply       % only matches the reply with our Ref
    after 5000 ->
        {error, timeout}
    end.

% Problem: other messages stay in the mailbox, slowing future receives.
% For high-throughput servers use a selective receive library or
% gen_server (which keeps its mailbox clean).

% Non-selective (drain in order):
drain_all() ->
    receive
        Msg -> [Msg | drain_all()]
    after 0 ->
        []
    end.

参照付きリクエスト/リプライ

call パターンは各リクエストを一意の参照(make_ref/0)とペアにします。クライアントは {call, Ref, self(), Request} を送信し同じ Ref のタグ付きリプライを待ちます——メールボックス内の他のトラフィックから自分のリプライを拾い出せます。Erlang の RPC と gen_server:call の基礎です。

erlang
% Client: send a tagged request, await the matching reply
call(Server, Request) ->
    Ref = make_ref(),
    Server ! {call, Ref, self(), Request},
    receive
        {Ref, Reply} -> Reply
    after 5000 ->
        exit(timeout)
    end.

% Server side:
handle({call, Ref, From, Request}, State) ->
    Reply = do_work(Request),
    From ! {Ref, Reply},
    State.

尾再帰とサーバーループ

BEAM プロセスは尾再帰で状態を保持します:ループは新しい状態を最後の式として自身を呼び出すため、スタックが成長しません。これは正統なステートフルサーバーパターンです——各 receive 句は更新された状態で loop/1 への尾呼び出しで終わります。

erlang
% A counter server — each message mutates state via recursion
start_counter(Init) -> spawn(fun() -> counter_loop(Init) end).

counter_loop(N) ->
    receive
        inc      -> counter_loop(N + 1);
        dec      -> counter_loop(N - 1);
        {get, From} -> From ! {value, N}, counter_loop(N);
        reset    -> counter_loop(0)
    end.

% Caller:
Pid = start_counter(0).
Pid ! inc.
Pid ! {get, self()}.
receive {value, V} -> io:format("count = ~p~n", [V]) end.
05

OTP gen_server(概要)

gen_server ビヘイビアの骨組み

-behaviour(gen_server) の実装は全必須コールバック関数の存在をコンパイラにチェックさせます。サーバー状態は init/1 -> handle_call/3(同期)/ handle_cast/2(非同期)-> terminate/2 を流れます。{local, Name} 登録でクライアントはアトムで呼べます。

erlang
-module(counter).
-behaviour(gen_server).
-export([start_link/0, inc/0, get/0]).
-export([init/1, handle_call/3, handle_cast/2,
         handle_info/2, terminate/2, code_change/3]).

start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, 0, []).
inc()        -> gen_server:cast(?MODULE, inc).
get()        -> gen_server:call(?MODULE, get).

init(N)              -> {ok, N}.
handle_call(get, _F, N)  -> {reply, N, N}.
handle_cast(inc, N)      -> {noreply, N + 1}.
handle_info(_, N)        -> {noreply, N}.
terminate(_, _)          -> ok.
code_change(_, N, _)     -> {ok, N}.

call と cast

call は同期的でサーバーがリプライするまで呼び出し側をブロックします(デフォルト 5s タイムアウト後は呼び出し側が終了)。cast は非同期で常に即座に ok を返します。call は参照付きリクエスト/リプライパターンに基づきます。

erlang
% Synchronous call — waits for reply, crashes on server death
{ok, Value} = gen_server:call(Server, {get, Key}).

% Asynchronous cast — fire and forget
ok = gen_server:cast(Server, {put, Key, Value}).

% Call supports timeout and monitor under the hood:
gen_server:call(Server, Req, 5000).

% Server replies in handle_call:
handle_call({get, Key}, From, State) ->
    {reply, maps:get(Key, State), State}.

% handle_cast never replies:
handle_cast({put, K, V}, State) ->
    {noreply, maps:put(K, V, State)}.

handle_info とその他のコールバック

handle_info/2 は非 call/cast メッセージを全てキャッチします——Pid ! Msg で送られたもの、'DOWN'、タイムアウト、ソケットデータ等。terminate/2 はリソース解放の場所ですが、強制 kill では実行保証されません。code_change/3 が Erlang の有名な無停止ホットコードアップグレードを支えます。

erlang
% handle_info/2 handles messages NOT from call/cast:
%   - plain messages from other processes (Pid ! Msg)
%   - 'DOWN' messages from monitors
%   - timeouts sent by gen_server:start(..., Timeout)
handle_info({'DOWN', Ref, process, Pid, Reason}, State) ->
    io:format("monitored ~p died: ~p~n", [Pid, Reason]),
    {noreply, State};
handle_info({tcp, Socket, Data}, State) ->
    %% handle incoming socket data
    {noreply, State};
handle_info(Info, State) ->
    {noreply, State}.

% terminate/2 runs on shutdown — close fds, flush, etc.
terminate(Reason, State) ->
    io:format("shutting down: ~p~n", [Reason]),
    ok.

% code_change/3 enables hot code upgrade.
code_change(_OldVsn, State, _Extra) -> {ok, State}.

アプリケーション構造(OTP)

OTP アプリケーションは自己完結コンポーネントで、.app リソースファイルがモジュール、依存関係、エントリモジュール(mod)を記述します。application ビヘイビアの start/2 がトップレベルスーパーバイザー PID を返し、それがワーカーを起動します。リリースがアプリをランナブルシステムにまとめます。

erlang
% myapp.app — application resource file
{application, myapp,
 [{description, "Demo app"},
  {vsn, "1.0.0"},
  {modules, [myapp_app, myapp_sup, counter]},
  {registered, [myapp_sup]},
  {applications, [kernel, stdlib]},
  {mod, {myapp_app, []}},
  {env, [{port, 4000}]}
 ]}.

% myapp_app.erl — application behaviour
-module(myapp_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) -> myapp_sup:start_link().
stop(_State) -> ok.

% Start everything:
%   application:start(myapp).
% Or with release:  erl -eval 'application:ensure_all_started(myapp)'

Was this helpful?