秘笈
2021 年 4 月 28 日
本節包含有助於喚起您記憶力,供在您對基本 Erlang 資料、類型或語法的記憶不太清晰時提醒使用。
資料類型
名稱 | 說明 | Dialyzer | 範例語法 |
---|---|---|---|
整數 | 沒有小數點的數字 | integer() 、pos_integer() 、non_neg_integer() | 1 、2 、3 、-213 、16#01FF 、2#101011 |
浮點數 | 具有小數點的數字 | float() | 1.0 、-1.0 、123.12 、1.0e232 |
數字 | 浮點數或整數 | number() | 1.0 , 1 |
原子 | 文字元、常數,其名稱為值 | atom() | abc 、'abc' 、some_atom@erlang 、'atom with spaces' |
布林值 | 原子 true 或 false | boolean() | true 、false |
參照 | 唯一的霧視值 | reference() | make_ref() |
函數 | 匿名函數 | fun() 、fun((ArgType) -> RetType) | <code>fun(X) -> X end, fun F(0) -> []; F(N) -> [1 | F(N-1)] end</code> |
埠 | 檔案描述符的不透明類型 | port() | N/A |
PID | 程序識別碼 | pid() | <0.213.0> |
元組 | 群組已知的一組元素 | tuple() 、{A, B, C} | {celsius, 42 }、{a, b, c} 、{ok, {X, Y}} |
映射 | 術語字典 | map() 、#{KType => VType} 、#{specific_key := VType} | #{a => b, c => d} 、Existing#{key := Updated} |
nil | 空清單 | [] | [] |
清單 | 用於術語清單的遞迴結構 | list() 、[Type] | [a, b, c] 、<code>[a | [b | [c | []]]]</code>、"a string is a list" |
二進制 | 平面位元組序列 | binary() | <<1,2,3,4>> 、<<"a string can be a binary">> 、<<X:Size/type, _Rest/binary>> |
術語排序:數字 < 原子 < 參照 < 函數 < 埠 < PID < 元組 < 映射 < nil < 清單 < 二進位
模組和語法
%%% This is a module-level comment
%%% @doc This tag includes officiel EDoc documentation.
%%% It can be useful for people to consule
%%% @end
%%% Generate documentation with rebar3 edoc
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Let's start with Module Attributes %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This is an attribute or function-specific comment
%% attributes start with a `-', functions with letters.
%% This file should be saved as `sample.erl'
-module(sample).
%% Functions are described in the form Name/Arity, and must
%% be exported through an `-export([...]).' module attribute
-export([f/0, f/1]).
-export([x/0]). % multiple export attributes can exist
%% You can "import" functions from another module, but
%% for clarity's sake (and because there's no namespaces)
%% nobody really does that
-import(module, [y/0]).
%% .hrl files contain headers, and are imported directly
%% within the module.
%% The following includes a private header file from src/
%% or a public header file from include/ in the current app
-include("some_file.hrl").
%% The following includes a public header file from the
%% include/ file of another application
-include_lib("appname/include/some_file.hrl").
%% specify an interface you implement:
-behaviour(gen_server).
%% Define a record (a tuple that compilers handles in a
%% special way)
-record(struct, {key = default :: term(),
other_key :: undefined | integer()}).
%% Just C-style macros
-define(VALUE, 42). % ?VALUE in this module becomes `42'
-define(SQUARE(X), (X*X)). % function macro
-define(DBG(Call), % a fancy debug macro: ?DBG(2 + 2)
io:format("DBG: ~s (~p): ~p~n",
[??Call, {?MODULE, ?LINE}, Call])).
%% Conditionals
-ifdef(MACRO_NAME). % opposite: -ifndef(MACRO_NAME).
-define(OTHER_MACRO, ok).
-else. % other option: -elif(NAME).
-define(MACRO_NAME, ok).
-endif.
%% Type definitions
-type my_type() :: number() | boolean().
-type my_container(T) :: {[T], [T], my_type(), mod:type()}
-export_type([my_type/0, my_container/1]).
%% you can also define custom attributes:
-my_attribute(hello_there).
-author("Duke Erlington").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% And now modules for code and functions %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @doc A function with 0 arguments returning an atom
-spec f() -> term(). % optional spec
f() -> ok.
-spec f(number()) -> float().
f(N) -> N + 1.0.
%% Pattern matching with clauses
x([]) -> []; % base recursive clause for a list
x([_H|T] -> [x | T]. % replace list element with `x' atom
%% @private variable binding rules
same_list(X = [_|_], X) -> true;
same_list([], []) -> true;
same_list(_, _) -> false.
%% Operators in the language
operators(X, Y) ->
+X, -Y, % unary
X + Y, X - Y, X * Y, X / Y, % any numbers
X div Y, X rem Y, % integers-only
X band Y, X bor Y, X bxor Y, % binary operators
X bsl Y, X bsr L, % bit shifting
not X, % boolean not
X andalso Y, X orelse Y, % shortcircuit boolean operators
X < Y, X > Y, X >= Y, X =< Y, % comparison
X == Y, X /= Y, % equality (float == int)
X =:= Y, X =/= Y, % strict equality (float =/= int)
X ++ Y, X -- Y, % append Y to X, delete Y from X
X ! Y. % send message Y to process X
%% Using guards. Valid guard expressions at:
%% erlang.org/doc/reference_manual/expressions.html#guard-sequences
comfortable({celsius, X}) when X >= 18, X =< 26 -> % AND clauses
true;
comfortable({celsius, _}) ->
false.
uncomfortable({celsius, X}) when X =< 18; X >= 26 -> % OR clauses
true;
uncomfortable({celsius, _}) ->
false.
%% difference with 'andalso' and 'orelse'
conds(X) when (is_number(X) orelse is_integer(X))
andalso X < 9 ->
%% equivalent (A AND B) OR C
true;
conds(X) when is_number(X); is_integer(X), X < 9 ->
%% - parentheses impossible with , or ;
%% - equivalent to A OR (B AND C)
true;
conds(T) when element(1, T) == celsius; is_integer(T) ->
%% element/2 extracts an element from a tuple. If `T' is
%% not a tuple, the call fails and `is_integer/1' is tried
%% instead
true;
conds(T) when element(1, T) == celsius orelse is_integer(T) ->
%% this can never work: if element/2 fails, the whole
%% `orlese' expressoin fails and `is_integer/1' is skipped
true.
%% Conditionals
conditional('if', Light) ->
if Light == red -> stop;
Light == green; Light == yellow -> go_fast;
true -> burnout % else clause!
end;
conditional('case', {Light, IsLate}) ->
case Light of
green -> go;
yellow when IsLate -> go_fast;
_ -> stop
end;
conditional(pattern, green) -> go;
conditional(pattern, yellow) -> slow;
conditional(pattern, red) -> stop.
%% List and binary comprehensions
comp(ListA, ListB) ->
[X*X || X <- ListA, X rem 2 == 0], % square even numbers
[{X,Y} || X <- ListA, Y <- ListB], % all possible pairs
<< <<X:8>> || X <- ListA >>. % turn list into bytes
comp(BinA, BinB) -> % now with binaries
<< <<X*X:32>> || <<X:8>> <= Bin, X rem 2 == 0 >>,
[{X,Y} || <<X:32>> <= BinA, <<Y:8>> <= BinB],
[X || <<X:8>> <= BinA].
%% Anonymous and higher order functions
higher_order() ->
If = fun(Light) -> conditional('if', Light) end,
Case = fun(Light) -> conditional('case', {Light, true}) end,
lists:map(If, [green, yellow, red]),
lists:map(Case, [green, yellow, red]),
If(red), % can be called literally
lists:map(fun(X) -> X*X end, [1,2,3,4,5]).
try_catch() ->
try
some_call(), % exceptions in this call are caught as well
{ok, val}, % common good return value to pattern match
{error, reason}, % common bad return value to pattern match
% any of these expression aborts the execution flow
throw(reason1), % non-local returns, internal exceptions
error(reason2), % unfixable error
exit(reason3) % the process should terminate
of % this section is optional: exceptions here are not caught
{ok, V} ->
do_something(V),
try_catch(); % safely recurse without blowing stack
{error, R} ->
{error, R} % just return
catch % this section is optional: various patterns
throw:reason1 -> handled;
reason2 -> oops; % never matches, `throw' is implicit type
error:reason2 -> handled;
exit:reason3 -> handled;
throw:_ -> wildcard_throws;
E:R when is_error(E) -> any_error;
_:_:S -> {stacktrace, S}; % extract stacktrace
after -> % this is an optional 'finally' block
finally
end.
程序和訊號
%% Start a new process
Pid = spawn(fun() -> some_loop(Arg) end)
Pid = spawn('[email protected]', fun() -> some_loop(Arg) end)
Pid = spawn(some_module, some_loop, [Arg])
Pid = spawn('[email protected]', some_module, some_loop, [Arg])
%% Spawn a linked process
Pid = spawn_link(...) % 1-4 arguments as with spawn/1-4
%% Spawn a monitored process atomically
{Pid, Ref} = spawn_monitor(fun() -> some_loop(Arg) end)
{Pid, Ref} = spawn_monitor(some_module, some_loop, [Arg])
%% Spawn with fancy options
spawn_opt(Fun, Opts)
spawn_opt(Node, Fun, Opts)
spawn_opt(Mod, Fun, Args, Opts)
spawn_opt(Node, Mod, Fun, Args, Opts)
%% Options must respect the following spec; many are advanced
[link | monitor |
{priority, low | normal | high | max} | % don't touch
{fullsweep_after, integer() >= 0} | % full GC
{min_heap_size, Words :: integer() >= 0} | % perf tuning
{min_bin_heap_size, Words} |
{max_heap_size, % heap size after which
Words | % the process may be killed. Use
#{size => integer() >= 0, % to indirectly set max queue sizes
kill => boolean(),
error_logger => boolean()}}
%% send an exit signal to a process
exit(Pid, Reason)
%% Receive a message
receive
Pattern1 when OptionalGuard1 ->
Expression1;
Pattern2 when OptionalGuard2 ->
Expression2
after Milliseconds -> % optional
Expression
end
%% Naming processes
true = register(atom_name, Pid)
true = unregister(atom_name)
Pid | undefined = whereis(atom_name)
%% Monitor
Ref = erlang:monitor(process, Pid)
true = erlang:demonitor(Ref)
true | false = erlang:demonitor(Ref, [flush | info])
%% Links
link(Pid)
unlink(Pid)
process_info(trap_exit, true | false)
這裡以圖解表達連結與監視器的語意

圖 1: 監視器為單向訊息訊號,而且會堆疊

圖 2: 未捕捉的連結為雙向,並會終止另一個處理程序,除非原因為「一般」

圖 3: 已捕捉的連結會轉換為訊息,但「終止」原因不可捕捉
OTP 處理程序由於監督的惡作劇,語意會略有不同

圖 4: 未捕捉的連結對 OTP 有相同作用

圖 5: 當處理程序的父項死亡時,已捕捉的連結會以特殊方式表現

圖 6: 依終止原因,監督會以不同方式記錄事件
行為
由於此處未列出所有 OTP 行為,僅列出最常使用的部分。
應用程式
觸發 | 由以下呼叫 | 由以下處理 | 回傳 | 說明 |
---|---|---|---|---|
application:start/1-2 | 用戶端或啟動的虛擬機器 | start(類型, 引數) | <程式碼>{ok, pid()} | {ok, pid(), 狀態}</程式碼> | 應該啟動根監督 |
{啟動階段, [{階段, 引數}]} 在 app 檔案 | 啟動 app 的核心 | start_phase(階段, 類型, 引數) | <程式碼>ok | {error, 原因}</程式碼> | 選用。可隔離特定初始化步驟 |
application:stop/1 | 關閉 app | prop_stop(狀態) | 狀態 | 選用。在關閉監督樹前呼叫 |
application:stop/1 | 關閉 app | stop(狀態) | term() | app 執行完畢後呼叫,用於清除垃圾 |
熱碼更新 | SASL 的釋出處理常式 | config_change(已變更::[{K,V}], 新的::[{K,V}], 已移除::[K]) | ok | 如果組態值變更,則使用虛擬機器 relup 功能進行熱碼更新後呼叫 |
監督
觸發 | 由以下呼叫 | 由以下處理 | 回傳 | 說明 |
---|---|---|---|---|
supervisor:start_link/2-3 | 父處理程序 | init(引數) | <程式碼>ignore | {ok, {SupFlag, [子項]}}</程式碼> | 指定監督。請參閱官方文件 |
gen_server
觸發 | 由以下呼叫 | 由以下處理 | 回傳 | 說明 |
---|---|---|---|---|
gen_server:start_link/3-4 | 監督 | init(引數) | <程式碼>{ok, 狀態 [, 選項]} | ignore | {stop, 原因}</程式碼> | 設定處理程序的初始狀態 |
gen_server:call/2-3 | 用戶端 | handle_call(訊息, 發訊者, 狀態) | <程式碼>{類別::回覆 | 無回覆, 狀態 [, 選項]} | {stop, 原因 [, 回覆], 狀態}</程式碼> | 請求/回覆模式。接受訊息並期待回答 |
gen_server:cast/2 | 用戶端 | handle_cast(訊息, 狀態) | <程式碼>{無回覆, 狀態 [, 選項]} | {stop, 原因, 狀態}</程式碼> | 傳送至處理程序的資訊;發射後即可忘記 |
Pid ! 訊息 | 用戶端 | handle_info(訊息, 狀態) | 與handle_cast/2 相同 | 帶外訊息,包括截取退出時的監控信號和'EXIT' 訊息 |
將選項 值設定為{繼續, Val} | 伺服器本身 | handle_continue(Val, State) | 與handle_cast/2 相同 | 用於將較長的作業劃分為可觸發的內部事件 |
gen_server:stop/1,3 | 客戶端或主管 | terminate(Reason, State) | term() | 在流程願意或經由錯誤關閉時呼叫。如果流程不會捕捉退出,可以省略這個回呼。 |
sys:get_status/2-3 、崩潰記錄 | 客戶端、伺服器本身 | <code>format_status(normal | terminate, [PDict, State])</code> | [{data, [{"State", Term}]}] | 用於新增或移除會加到除錯呼叫或錯誤記錄的資訊 |
N/A | 監督 | code_change(OldVsn, State, Extra) | {ok, NewState} | 在執行帶有版本更新的熱程式碼升級期間提供適當的說明時,呼叫以更新有狀態流程 |
gen_statem
流程管理
觸發 | 由以下呼叫 | 由以下處理 | 回傳 | 說明 |
---|---|---|---|---|
gen_statem:start_link/3-4 | 監督 | init(引數) | <code>{ok, State, Data [, Actions]} | ignore | {stop, Reason}</code> | 設定狀態機器初期的狀態和資料 |
N/A | 內部 | callback_mode() | <code>[state_functions | handle_event_function [, state_enter]]</code> | 定義 FSM 的類型以及進入狀態是否觸發特殊的內部事件 |
gen_statem:stop/1,3 | 客戶端或主管 | terminate(Reason, State, Data) | term() | 在流程願意或經由錯誤關閉時呼叫。如果流程不會捕捉退出,可以省略這個回呼。 |
sys:get_status/2-3 、崩潰記錄 | 客戶端、伺服器本身 | <code>format_status(normal | terminate, [PDict, State, Data])</code> | [{data, [{"State", Term}]}] | 用於新增或移除會加到除錯呼叫或錯誤記錄的資訊 |
N/A | 監督 | code_change(OldVsn, State, Data, Extra) | {ok, NewState, NewData} | 在執行帶有版本更新的熱程式碼升級期間提供適當的說明時,呼叫以更新有狀態流程 |
狀態處理和狀態轉換
由 handle_event/4
或 StateName/3
函式處理,依據 callback_mode()
的值。函式簽章可能是
handle_event(EventType, EventDetails, State, Data)
State(EventType, EventDetails, Data)
如果 State
的值不是清單,即使 callback_mode()
定義了 state_functions
,也會呼叫 handle_event/4
。這兩個函式的所有可能回傳值之一是
{next_state, State, Data}
{next_state, State, Data, [Actions, ...]}
{stop, Reason, Data}
{stop, Reason, Data, [Actions, ...]}
有許多簡寫形式,例如 keep_state_and_data
、{keep_state, Data}
、{repeat_state, Data}
和許多更多。請參閱文件以了解其內容。
Actions
值是下列清單的任意組合(非包含):postpone
、{next_event, EventType, EventDetails}
、hibernate
、{timeout, Delay, EventDetails}
、{state_timeout, Delay, EventDetails}
、{reply, From, Reply}
、hibernate
。請諮詢文件以取得更多選項。
觸發 | 由以下呼叫 | 事件類型 | 事件明細 | 說明 |
---|---|---|---|---|
gen_statem:call/2-3 | 用戶端 | {call, From} | term() | 要求/回應模式。收到一則訊息,預期會收到回覆 |
gen_statem:cast/2 | 用戶端 | 投遞 | term() | 必須將資訊傳送給流程;執行後就忘記 |
Pid ! 訊息 | 用戶端 | 資訊 | 訊息 | 帶外訊息,包括監控訊息和捕捉的 'EXIT' 訊號 |
{timeout, T, Msg} | Action 回傳值 | 逾時 | 訊息 | 一個特定逾時值,可以在狀態機器在 T 毫秒內沒有收到新事件時設定和內部接收。 |
{state_timeout, T, Msg} | Action 回傳值 | state_timeout | 訊息 | 當狀態機在多少毫秒內 T 沒有轉換到新的不同狀態時,就可以設定並接收到特定的逾時 |
{next_event, internal, Msg} | Action 回傳值 | 內部 | 訊息 | 由狀態機產生的內部訊息,希望自行觸發,而且看起來不像是外部呼叫 |