速查表
2021年4月28日
本节包含各种提示,如果您对基本的 Erlang 数据、类型或语法不太熟悉,可以帮助您回忆。
数据类型
名称 | 描述 | Dialyzer | 示例语法 |
---|---|---|---|
整数 (integer) | 没有小数点的数字 | integer() , pos_integer() , non_neg_integer() | 1 , 2 , 3 , -213 , 16#01FF , 2#101011 |
浮点数 (float) | 带有小数点的数字 | float() | 1.0 , -1.0 , 123.12 , 1.0e232 |
数字 (number) | 浮点数或整数 | number() | 1.0 , 1 |
原子 (atom) | 文字常量,使用其自身名称表示值 | atom() | abc , 'abc' , some_atom@erlang , 'atom with spaces' |
布尔值 (boolean) | 原子 true 或 false | boolean() | true , false |
引用 (reference) | 唯一的不透明值 | reference() | make_ref() |
函数 (fun) | 匿名函数 | fun() , fun((ArgType) -> RetType) | <code>fun(X) -> X end, fun F(0) -> []; F(N) -> [1 | F(N-1)] end</code> |
端口 (port) | 文件描述符的不透明类型 | port() | N/A |
进程 ID (pid) | 进程标识符 | pid() | <0.213.0> |
元组 (tuple) | 对一组已知元素进行分组 | tuple() , {A, B, C} | {celsius, 42 }, {a, b, c} , {ok, {X, Y}} |
映射 (map) | 项的字典 | map() , #{KType => VType} , #{specific_key := VType} | #{a => b, c => d} , Existing#{key := Updated} |
空 (nil) | 空列表 | [] | [] |
列表 (list) | 项列表的递归结构 | list() , [Type] | [a, b, c] , <code>[a | [b | [c | []]]]</code>, "a string is a list" |
二进制 (binary) | 扁平的字节序列 | binary() | <<1,2,3,4>> , <<"a string can be a binary">> , <<X:Size/type, _Rest/binary>> |
项排序:number < atom < reference < fun < port < pid < tuple < map < nil < list < binary
模块和语法
%%% 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)
以及链接和监视器的语义,以图表形式表示
由于监督机制,OTP 进程具有略微不同的语义
行为
此处未列出所有 OTP 行为,仅列出了最常用的行为。
应用程序
触发器 | 调用方 | 处理方 | 返回值 | 描述 |
---|---|---|---|---|
application:start/1-2 | 客户端或启动虚拟机 | start(Type, Args) | <code>{ok, pid()} | {ok, pid(), State}</code> | 应启动根监督器 |
{start_phases, [{Phase, Args}]} 在 app 文件中 | kernel 启动应用程序 | start_phase(Phase, Type, Args) | <code>ok | {error, Reason}</code> | 可选。可以隔离初始化的特定步骤 |
application:stop/1 | 应用程序关闭 | prop_stop(State) | State | 可选。在关闭监督树之前调用 |
application:stop/1 | 应用程序关闭 | stop(State) | term() | 应用程序运行完成后调用,以清理内容 |
热代码更新 | SASL 的发布处理程序 | config_change(Changed::[{K,V}], New::[{K,V}], Removed::[K]) | ok | 使用虚拟机的 relup 功能进行热代码更新后调用,如果配置值已更改 |
监督器
触发器 | 调用方 | 处理方 | 返回值 | 描述 |
---|---|---|---|---|
supervisor:start_link/2-3 | 父进程 | init(Arg) | <code>ignore | {ok, {SupFlag, [Child]}}</code> | 指定监督器。请参阅官方文档 |
gen_server
触发器 | 调用方 | 处理方 | 返回值 | 描述 |
---|---|---|---|---|
gen_server:start_link/3-4 | 监督器 | init(Arg) | <code>{ok, State [, Option]} | ignore | {stop, Reason}</code> | 设置进程的初始状态 |
gen_server:call/2-3 | 客户端 | handle_call(Msg, From, State) | <code>{Type::reply | noreply, State [, Option]} | {stop, Reason [, Reply], State}code> | 请求/响应模式。接收消息并期望得到答案 |
gen_server:cast/2 | 客户端 | handle_cast(Msg, State) | <code>{noreply, State [, Option]} | {stop, Reason, State}</code> | 发送到进程的信息;即发即忘 |
Pid ! Msg | 客户端 | handle_info(Msg, State) | 与 handle_cast/2 相同 | 带外消息,包括监视器信号和捕获退出时的 'EXIT' 消息 |
将 Option 值设置为 {continue, 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(Arg) | <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 | 客户端 | cast | term() | 必须将信息发送到进程;即发即忘 |
Pid ! Msg | 客户端 | info | Msg | 带外消息,包括监视器消息和捕获的 'EXIT' 信号 |
{timeout, T, Msg} | Action 返回值 | timeout | Msg | 可以在状态机在 T 毫秒内未收到新事件时内部设置和接收的特定超时 |
{state_timeout, T, Msg} | Action 返回值 | state_timeout | Msg | 可以在状态机在 T 毫秒内未转换到新的不同状态时内部设置和接收的特定超时 |
{next_event, internal, Msg} | Action 返回值 | 内部 | Msg | 状态机想要触发自身而不像外部调用时可以生成的内部消息 |