Erlang 应用程序通常使用标准行为,例如gen_server http://www.erlang.org/doc/man/gen_server.html and gen_fsm http://www.erlang.org/doc/man/gen_fsm.html,它已经在其内部循环中包含完全限定的函数调用,因此可以解决这个问题。
但是,如果出于某种原因,您觉得必须使用自己的递归消息处理循环来编写自己的模块,并且希望该模块在运行时可升级,则该循环需要包含完全限定的递归调用,通常您会放置此在处理特定升级消息的代码段中,类似于code_change/3功能 http://www.erlang.org/doc/man/gen_server.html#Module:code_change-3预期在与标准行为一起使用的回调模块中。
例如,考虑下面的循环,它与标准行为的循环类似,但大大简化了:
loop(Callbacks, State) ->
{{Next, NState},DoChange} =
receive
{code_change, ChangeData} ->
{Callbacks:handle_code_change(ChangeData, State), true};
{cast,Data} ->
{Callbacks:handle_cast(Data,State), false};
{call,From,Data} ->
Result = Callbacks:handle_call(Data,State),
case Result of
{reply, Reply} ->
From ! Reply;
_ ->
ok
end,
{Reply, false};
Message ->
{Callbacks:handle_info(Message,State), false}
end,
case Next of
stop -> ok;
_ ->
case DoChange of
true -> ?MODULE:loop(Callbacks, NState);
false -> loop(Callbacks, NState)
end
end.
The loop/2
函数有两个参数:Callbacks
,预期导出为特定消息调用的特定函数的回调模块的名称,以及State
,这对循环来说是不透明的,但可能对回调模块有意义。该循环是尾递归的,它通过调用特定的回调函数来处理几个特定的消息,然后通过调用来处理任何其他消息handle_info/2
在回调模块中。 (如果您使用过标准行为,您会发现这种方法很熟悉。)回调函数返回一个Next
值,以及要传递到下一个循环的新状态。如果Next
is stop
,我们退出循环,否则我们检查DoChange
,设置为true
仅适用于代码更改消息,如果为真,则循环使用完全限定的调用来调用自身,否则它仅使用常规调用。
正如前面提到的,这一切都被大大简化了。您很少需要编写自己的循环,如果您这样做,还有其他重要的事情未在此处显示,例如系统消息 http://www.erlang.org/doc/design_principles/spec_proc.html你需要处理的。您最好使用标准行为。