Erlang 应用程序通常使用gen_server 和gen_fsm 等标准行为,它们的内部循环中已经包含完全限定的函数调用,因此请注意这个问题。
但是,如果由于某种原因,您不得不使用自己的递归消息处理循环编写自己的模块,并且希望该模块在运行时可升级,则循环需要包含完全限定的递归调用,通常您d 将其放在处理特定升级消息的代码部分中,类似于与标准行为一起使用的回调模块中预期的code_change/3 function。
例如,考虑下面的循环,它类似于标准行为,但大大简化:
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.
loop/2 函数有两个参数:Callbacks,一个回调模块的名称,期望导出为特定消息调用的特定函数,以及State,它对循环不透明,但可能对回调模块有意义.循环是尾递归的,它通过调用特定的回调函数来处理几个特定的消息,然后通过调用回调模块中的handle_info/2 来处理任何其他消息。 (如果您使用过标准行为,您会发现这种方法很熟悉。)回调函数返回一个Next 值,以及一个要传递到下一个循环的新状态。如果Next 是stop,我们退出循环,否则我们检查DoChange 的值,它设置为true 仅用于代码更改消息,如果为真,则循环使用完全限定的调用调用自身,否则它只使用常规调用。
如前所述,这一切都大大简化了。您需要编写自己的循环非常罕见,如果您这样做,还有其他重要的事情未在此处显示,例如您需要处理的system messages。你最好使用标准行为。