Используя полностью квалифицированные вызовы функций в Erlang?
Я только что узнал, как обновить модуль в Erlang, и я знаю, что только вызовы функций, которые используют полные имена (например, module:function()
) "связывается" с текущей версией, загруженной в ВМ, но вызовы функций, которые не указывают имя модуля, не "связываются" с текущей версией, но продолжают использовать более старую версию.
Существует ли практическое правило, когда следует использовать полный вызов функции и когда можно вызывать функцию только по ее имени? Это плохая идея вызывать все функции, используя их полное имя (например, module:function()
)?
1 ответ
Приложения Erlang обычно используют стандартное поведение, такое как gen_server
а также gen_fsm
, которые уже содержат полностью определенные вызовы функций внутри своих внутренних циклов и поэтому решают эту проблему.
Но если по какой-то причине вы чувствуете необходимость написать свой собственный модуль со своим собственным рекурсивным циклом обработки сообщений и хотите, чтобы этот модуль можно было обновлять во время выполнения, цикл должен содержать полностью определенный рекурсивный вызов, и обычно вы делаете это в разделе кода, обрабатывающего конкретное сообщение об обновлении, аналогично 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.
loop/2
Функция принимает два аргумента: Callbacks
имя модуля обратного вызова, который должен экспортировать определенные функции, вызываемые для определенных сообщений, и State
, который непрозрачен для цикла, но, вероятно, имеет смысл для модуля обратного вызова. Цикл является хвостовым рекурсивным и обрабатывает несколько определенных сообщений, вызывая определенные функции обратного вызова, а затем обрабатывает любые другие сообщения, вызывая handle_info/2
в модуле обратного вызова. (Если вы использовали стандартное поведение, вы найдете этот подход знакомым.) Функции обратного вызова возвращают Next
значение и новое состояние для передачи в следующий цикл. Если Next
является stop
, мы выходим из цикла, в противном случае мы проверяем значение DoChange
, который установлен в true
только для сообщений об изменении кода, и если это так, цикл вызывает себя с помощью полностью определенного вызова, в противном случае он использует только обычный вызов.
Как упоминалось ранее, все это значительно упрощается. Крайне редко вам нужно писать свои собственные циклы, и если вы делаете это, есть другие важные вещи, которые здесь не показаны, например системные сообщения, с которыми вам нужно иметь дело. Вам лучше всего использовать стандартное поведение.