[erlang-questions] one-time evaluated functions
Richard A. O'Keefe
ok@REDACTED
Tue Oct 31 03:26:31 CET 2017
Some obvious questions:
- when does this make sense?
As others have noted, the function must be pure.
The cost of re-evaluating the function should
also be significantly higher than the cost of
checking, in the context of the whole program.
- how much does it actually save?
This depends of course on the actual function.
It also depends on how much the compiler can
optimise the function, so the way the function
is written may be significant.
- is there prior art for this?
Eiffel has "once" functions. In fact it has two
kinds: once per class and once per instance. In
Erlang, the equivalent might be that it makes as
much sense, if not more, to cache f(X) as f().
It also reminds us that there are at least two
versions that might make sense for Erlang: once
per module, and once per process.
Smalltalk historically made much use of
"lazy initialisation" where you do
cachedValue ifNil: [cachedValue := ...].
I used that a lot in my Smalltalk system, up
to the point where I implemented threads. And
then, mindful of the "double-checked locking is
broken" issue, changed it all to load-time
initialisation. The one exception is encoding
and decoding tables, which are loaded on demand,
and protected by a lock. In this context, the
cost of locking was negligible.
What do we learn from this?
(1) It would be possible to have
-once(f/0, module).
f() -> ...
implemented by having the loader load the module
as usual, call f() getting V, and then patch the
module to f() -> V.
Others have explained using ETS tables, which has
higher overhead, but can be done right now.
(2) It is possible to simulate
-once(f/0, process).
by having
f() ->
case get({?MODULE,f})
of undefined ->
V = real_f(),
put({?MODULE,f}, [V])
; [V] ->
ok
end,
V.
real_f() ->
whatever f()'s body would have been.
This could be done using a parse transform.
(3) Concrete details matter.
More information about the erlang-questions
mailing list