[erlang-questions] Process scope variable
Richard A. O'Keefe
ok@REDACTED
Wed Feb 18 03:29:13 CET 2015
On 18/02/2015, at 2:05 pm, Imants Cekusins <imantc@REDACTED> wrote:
> > It has problems, but it can do the job, and the problems are a
> helpful reminder that maybe the job should not be done.
>
> Could you go into more detail about the problems with the process dictionary, please?
I already did.
(1) It's mutable.
You can sort of fix this by using wrapper functions:
safe_put(Key, Val) ->
undefined = get(Key),
put(Key, Val).
(2) Keys are global, not per-module.
If you call code from a module that you have not read,
you cannot tell which keys it might use for its own purposes.
You can sort of fix this by using module_name.variable_name keys.
As it happens, the Erlang libraries DO use the process
dictionary, most notably for holding the random generator state.
The number of keys used is in the low hundreds, and *most* of
them are safely hidden inside other processes, but I don't
KNOW which are and which are not.
If your process-scope variables are per-module, then a lot of
the problem goes away. (In particular, when I last looked,
nothing in the Erlang libraries used M.V style keys.) And
then you have the problem of not being able to refer to a
variable in another module when you want to, which guess what,
*prevents* some useful refactorings.
(3) There is as far as I know no tool support to help with (2) or
even with tracking key use within a single module. Since ANY
term may be used as a key, things like
receive Key -> receive Val -> put(Key, Val) end end
are possible, meaning that as far as I can tell, it isn't
even theoretically possible to be complete and correct. Some
kind of approximate (ideally conservative) tool would be nice.
This can be worked around by writing a trivial preprocessor.
This one is in AWK to keep the message short.
/^-[ \t]*module[(]/ {
module = $0
sub(/^-[ \t]*module[(][ \t]*/, "", module)
sub(/[^a-zA-Z0-9_.].*$/, "", module)
print
next
}
/[ \t](get|put)[(][.][a-z]/ {
key = $0
sub(/^.*[ \t](get|put)[(][.]/, "", key)
sub(/[^a-zA-Z0-9_.].*$/, "", key)
tally[key]++
match($0, /[(][.]/)
print substr($0, 1, RSTART) module substr($0, RSTART+1)
next
}
{
print
}
END {
for (key in tally) printf "%3d %s\n", tally[key], key >"/dev/stderr"
}
will turn
-module(fee).
-export([fie/0, foh/0, fum/0]).
fie() ->
put(.key, 1).
foh() ->
N = get(.key),
put(.key, N+1).
fum() ->
get(.key).
into
-module(fee).
-export([fie/0, foh/0, fum/0]).
fie() ->
put(fee.key, 1).
foh() ->
N = get(fee.key),
put(fee.key, N+1).
fum() ->
get(fee.key).
and write
4 key
to stderr, so I can see what keys are used this way.
THIS IS JUST A TOY EXAMPLE. I DO NOT USE IT IN PRACTICE.
(4) There is the memory leak problem. If you put something in the process
dictionary, it will stay there until it is explicitly removed. There
is no automatic garbage collection of keys from the process dictionary.
While you have not presented a detailed specification of process scope
variables, it looks like they would be even *worse* in this regard,
because at least there is erase(dead_key). What would you use for
oddball variables?
More information about the erlang-questions
mailing list