Local calls, remote calls, closure calls, tuple calls, p-mod calls. The code server. Linking. Hot code loading, purging. (Overlap with Chapter 4, have to see what goes where.) Higher order functions, Implementation of higher order functions. Higher order functions and hot code loading. Higher order functions in a distributed system.
In Erlang there is a semantic difference between a local function call and a remote function call. A remote call, that is a call to a function in a named module, is guaranteed to go to the latest loaded version of that module. A local call, an unqualified call to a function within the same module, is guaranteed to go to the same version of the code as the caller.
A call to a local function can be turned into a remote call by specifying the module name at the call site. This is usually done with the ?MODULE macro as in ?MODULE:foo(). A remote call to a non local module can not be turned into a local call, i.e. there is no way to guarantee the version of the callee in the caller.
This is an important feature of Erlang which makes hot code loading or hot upgrades possible. Just make sure you have a remote call somewhere in your server loop and you can then load new code into the system while it is running; when execution reaches the remote call it will switch to executing the new code.
A common way of writing server loops is to have a local call for the main loop and a code upgrade handler which does a remote call and possibly a state upgrade:
loop(State) ->
receive
upgrade ->
NewState = ?MODULE:code_upgrade(State),
?MODULE:loop(NewState);
Msg ->
NewState = handle_msg(Msg, State),
loop(NewState)
end.
With this construct, which is basically what gen_server
uses,
the programmer has control over when and how a code upgrade is done.
The hot code upgrade is one of the most important features of Erlang
which makes it possible to write servers that operates 24/7 year out
and year in. It is also one of the main reasons why Erlang is
dynamically typed. It is very hard in a statically typed language to
give type for the code_upgrade
function. (It is also hard to give the
type of the loop function). These types will change in the future as
the type of State changes to handle new features.
For a language implementer concerned with performance, the hot code loading functionality is a burden though. Since each call to or from a remote module can change to new code in the future it is very hard to do whole program optimization across module boundaries. (Hard but not impossible, there are solutions but so far I have not seen one fully implemented).
In the Erlang Runtime System the code loading is handled by the code server. The code server will call the lower level BIFs in the erlang module for the actual loading. But the code server also determines the purging policy.
The runtime system can keep two versions of each module, a current version and an old version. All fully qualified (remote) calls go to the current version. Local calls in the old version and return addresses on the stack can still go to the old version.
If a third version of a module is loaded and there are still processes running (have pointers on the stack to) the code server will kill those processes and purge the old code. Then the current version will become old and the third version will be loaded as the current version.