Doubt about funs

Erik Johansson happi@REDACTED
Wed Feb 21 00:46:23 CET 2001

"Martin Bjorklund" wrote
> > F = fun (X1, ..., Xn) -> m:f(X1, ..., Xn) end
> I strongly disagree.  These two constructs behave very different!
> Using F in the former construct is an external apply, which means that
> the latest version of the code gets called.  Using F in the latter, on
> the other hand, is a call to the function defined in the version of
> the module that was there when F was constructed.  Thus, if a new
> version of the module (where the fun was defined) is loaded, and you
> try to use F, you'll using the old version.  If yet another version of
> the module is loaded, F is invalid, and the process is killed.

You have a point there, but your statement is not completely true. What you
describe will happen in the current implementation if the function defining
F is changed (or any funs are added or removed before the fun defining F).
But if you do the code change carefully you can actually change code in the
module defining F and F will upgrade to new code.

The point is really that you should not hold on to funs over code change.
But there is no reason to do so either. If the code defining the fun is
changed then the code that applies the fun should get a new fun.
You could run in to similar problems with F = {m,f} if you change the module
m so that f does not exist, or the arity of f changes, or the implementation
of f changes, then applying F is not a good idea.

It might even be an argument for using a fun instead of a tuple in the
current implementation:
 * If a process is holding on to a fun and the implementation of the fun is
changed twice then that process is killed, and the supervisor can start up
the process again in a correct way.
 * If a process is holding on to a mod-fun-tuple then the run-time system
can't detect this and the process will keep on living and applying {m, f}
even if the intention of the new code would be to apply {m, g}.

(The semantics of funs and code-change is an interesting topic and any input
is welcome. ;)

> >  (For the same reason, it is better to use spawn/1 and spawn/2 than the
old spawn/3 and
> > spawn/4, if possible. Avoid `apply/3'.)
> No!  Using spawn/1 or spawn/2 is even worse if you want to support
> code upgrade.  This means that the process will have a reference to
> the fun on it's stack, which it never will get rid of.  Thus, loading
> two more versions of the module kills the process.

No! That is not true.
(Unless the fun is not tail-recursive, but that is not specific for funs.
 If you want to support code upgrade you will use tail-recursive functions
as much as possible anyway.)

spawn( fun () -> m:f(X1, ..., Xn) end)
 has the same effect as
spawn(m, f, [X1, ..., Xn])

With the added benefit that you in the module m can write
spawn( fun () -> f(X1, ..., Xn) end)
without exporting f/n.

> Use funs with care.

Yes that is certainly true.
But as Richard wrote there are several advantages of using funs over a
tuple, and I can see no real advantage of using the tuple approach.
Correct behaviour for code change can just as easily be achieved with funs
as with "apply tuple"; you just need to think about it in a new way.

I hope you can see my point through my muddy presentation; it has been a
long day.


More information about the erlang-questions mailing list