Используя полностью квалифицированные вызовы функций в 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 только для сообщений об изменении кода, и если это так, цикл вызывает себя с помощью полностью определенного вызова, в противном случае он использует только обычный вызов.

Как упоминалось ранее, все это значительно упрощается. Крайне редко вам нужно писать свои собственные циклы, и если вы делаете это, есть другие важные вещи, которые здесь не показаны, например системные сообщения, с которыми вам нужно иметь дело. Вам лучше всего использовать стандартное поведение.

Другие вопросы по тегам