3

I am not sure how to call a local function in a module so that after the code change the latest version of the code will be used. See the following example:

1  -module(test).
2
3  -export([start/0, call/1]).
4  -export([loop/0, add/1]).
5
6  start() ->
7      register(foo, spawn(test, loop, [])).
8
9  call(X) ->
10     foo ! {self(), X},
11     receive
12         Y -> Y
13 end.
14
15 loop() ->
16     receive
17         {Pid, Z} -> Pid ! add(Z)
18     end,
19     loop().
20
21 add(N) ->
22     N + 1.

The function that will be changed is add/1. In order to use the latest version of the function, the call of add/1 (line 17) should be fully qualified function call {Pid, Z} -> Pid ! ?MODULE:add(Z). When I try it, I get this:

1> c(test). 
{ok,test}
2> test:start(). 
true
3> test:call(1).
2

line 22 changed to N + 2

4> c(test).     
{ok,test}
5> test:call(1).
3

line 22 changed again to N + 3

6> c(test).     
{ok,test}
7> test:call(1).
** exception error: bad argument
    in function  test:call/1 (test.erl, line 10)

Why do I get this error?

2240
  • 1,547
  • 2
  • 12
  • 30
juro
  • 631
  • 5
  • 13

1 Answers1

4

I believe that you need to eventually call the fully qualified version of the loop/0 function instead of the add/1 function in order to load and use the new module. The code loading mechanism is prepared to handle two running versions of a module at once, and your example with N+3 is the third load of the module -- and the first versions is forcibly removed.

Try instead this loop:

15 loop() ->
16     receive
17         {Pid, Z} -> Pid ! add(Z)
18     end,
19     ?MODULE:loop().

I've changed it to reload the newest version on next execution of the loop/0.

I believe more common is to use a reload message or similar that will directly call the main loop explicitly, to avoid the overhead of constantly reloading the module on every request.

sarnold
  • 102,305
  • 22
  • 181
  • 238
  • It works, thanks! So I guess that the `loop/1` didn't change and after the third change it was erased. But still I don't understand why `?MODULE:loop()` caused loading of a new version of `add/1` and why `?MODULE:add()` didn't do the same with `loop/1`. – juro Jun 14 '12 at 22:37
  • I think the trick is `?MODULE:add()` was going to execute to completion and then that module would evaporate. Don't forget that you might have thousands of processes executing in this module and you wouldn't want to change the code out from underneath them all -- only the ones that wanted to change. – sarnold Jun 14 '12 at 23:08
  • 2
    Document about code replace is: Erlang -- Compilation and Code Loading http://www.erlang.org/doc/reference_manual/code_loading.html#id83918 – shino Jun 15 '12 at 00:35