[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] ->

     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