erlangfully-qualified-naming

Using fully qualified function calls in Erlang?


I have just learnt how to upgrade a module in Erlang and I know that only the function calls that use the fully qualified names (eg. module:function()) gets "relinked" to the current version loaded into the VM, but the function calls that do not specify the module's name do not get "relinked" to the current version, but keep using the older one.

Is there a rule of thumb on when to use a fully qualified function call and when it's OK to call a function just by its name? Is it a bad idea to call all functions using their full name (like module:function())?


Solution

  • Erlang applications normally make use of standard behaviors like gen_server and gen_fsm, which already contain fully qualified function calls within their internal loops and so take care of this issue.

    But if for some reason you feel compelled to write your own module with its own recursive message-handling loop and you want that module to be upgradeable at runtime, the loop needs to contain a fully qualified recursive call, and normally you'd place this within a section of code handling a specific upgrade message, similar to the code_change/3 function expected in a callback module used with a standard behavior.

    For example, consider the loop below, which is similar to those of the standard behaviors but greatly simplified:

    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 function takes two arguments: Callbacks, the name of a callback module expected to export specific functions invoked for specific messages, and State, which is opaque to the loop but presumably meaningful to the callback module. The loop is tail recursive and it handles several specific messages by calling specific callback functions, and then handles any other messages by calling handle_info/2 in the callback module. (If you've used the standard behaviors you'll find this approach familiar.) The callback functions return a Next value, and a new state to be passed to the next loop. If Next is stop, we exit the loop, otherwise we check the value of DoChange, which is set to true only for code change messages, and if it's true the loop calls itself with a fully qualified call, otherwise it uses just a regular call.

    As mentioned earlier, this is all greatly simplified. It's extremely rare that you would need to write your own loops, and if you do there are other important things not shown here like system messages that you need to deal with. You are best off using the standard behaviors.