Erlang実装パターン2
id:voluntas からskypeで教わった話も一緒に入れています。
EDocスタイルの型宣言
コメントで@spec, @typeというのを使って型の宣言をする。ドキュメントジェネレータ、静的プログラムチェックツールで使用される。これを言語仕様として拡張して入れてしまおうというEEP-8(http://articles.shibu.jp/article/30833304.html)もある。ただし、使っているのはRabbitMQぐらいらしい。EUnitはEDoc。
@typeは型の宣言。@specは関数の入出力の宣言に使うみたい。typeは関数宣言部でも@specの補助として使えるっぽい。@throwsを使うと、例外が投げられる可能性についても書ける。
%% @type moduleName() = atom() %% @type functionName() = atom() %% @throws {bad_test, term()} %% | {generator_failed, exception()} %% @spec (testIterator()) -> [integer()] iter_id(#iter{pos = N, parent = Ns}) -> lists:reverse(Ns, [N]).
配列の操作。
nextが空か、そうじゃないかで関数を分けている。典型的なパターン。
# python def iter_next(I): if len(I.next) == 0: 空のときの処理 else: そうじゃないときの処理
関数直下のif文はこのように分解する。もちろん、caseも使えるはず。
%% Erlang %% @spec (testIterator()) -> none | {testItem(), testIterator()} iter_next(I = #iter{next = []}) -> nextが空の時の処理; iter_next(I = #iter{next = [T | Ts]}) -> nextが空じゃない時の処理.
if文はcaseに置き換える
Pythonの場合はこう。
if is_string(S): true時の処理 else: そうではない時の処理
Erlangだとこうかく。
case is_string(S) of true -> true時の処理; false -> そうではない時の処理; end
例外処理
try 正常時の文 catch 例外の型 -> キャッチしたときの処理 end
throw({型のアトム, 追加情報})
で例外を投げる。
文字列の結合
"test: " ++ S
と結合できる。
try ofという書き方もある
もちろん、 ->をたくさん書けば返り値でパターンマッチで処理の振り分けもできる。ただし、こういうコードは今のところお目にかかったことがない。プログラミングErlangの本の中にもない。 id:voluntas によると、これをやるぐらいなら、tryの返り値に対して、case ofを使った方がいいよ、とのこと。僕もそう思う。
try M:module_info(exports) of Es -> Fs = get_module_tests_1(M, Es), W = ?DEFAULT_MODULE_WRAPPER_NAME, case lists:member({W,1}, Es) of false -> Fs; true -> {generator, fun () -> M:W(Fs) end} end catch error:undef -> throw({module_not_found, M}) end.
宣言的なコメント
将来はこうしたいけど、今はうまくいかないからこっちを使っている、というコメント。テスティングフレームワークみたいに、自分で自分のテストなど、ドッグフード系のシステム開発にはこういうのは不可欠かも。Erlangに限った話ではないけど。
%%{application, eunit}. % this currently causes a loop %% We use the below until loop detection is implemented
タプルをそのまま画面に出力できない
io:format(atom).
はOKだけど
io:format({atom, "sample test"}).
はエラーで落ちる。ちなみに、formatは引数を一つだけとれる。一つしか取れない。
whenはパターンマッチ後に使える
whenを指定すると、パターンマッチの情報を詳細に指定できる。whenにはオリジナルの関数はつっこめないけど、マクロはOK。