[erlang-questions] Access to process dictionary

zxq9@REDACTED zxq9@REDACTED
Mon Dec 24 03:24:39 CET 2018


On 2018年12月23日日曜日 8時02分56秒 JST Donald Steven wrote:
> So I needed to know where in the input buffer I was; that is, to save 
> the buffer index.  In a procedural language, I'd just have a global 
> index and increment it after a getc and decrement it after a put back 
> character.  With a process dictionary I was able to do this.  Without 
> it, I was stumped.
> 
> 
> If you have a non-process-dictionary solution, I'm all ears.


Consider this simple state-keeping service loop:


-module(simple).
-export([start/1, stop/1]).
-export([add/2, sub/2]).

start(X) ->
  spawn(fun() -> loop(X) end).

add(PID, Amount) ->
    PID ! {add, Amount},
    ok.

sub(PID, Amount) ->
    PID ! {sub, Amount},
    ok.

stop(PID) ->
    PID ! stop,
    ok.

loop(X) ->
    ok = io:format("~p: X is ~p~n", [self(), X]),
    receive
        {add, Y} ->
            NewX = X + Y,
            loop(NewX);
        {sub, Y} ->
            NewX = X - Y,
            loop(NewX);
        stop ->
            ok = io:format("Bye!~n"),
            exit(normal);
        Unexpected ->
            ok = io:format("I don't understand ~tp~n", [Unexpected]),
            loop(X)
    end.


This process spawns with whatever value we give the start/1 function and keeps it in state as a state variable, not in the process dictionary. The "life" of this process is the main service loop, loop/1, and the add/2, sub/2, and stop/1 functions are all just functionally defined interfaces to make interacting with the process sane (blasting naked messages from random bits of code all over a project quickly becomes an exploding complexity problem).

This program just keeps looping, retaining the current value throughout its life until you send it a `stop` message.

Every process is a different little guy, all his own and has no visibility or concern about whatever is going on with other processes. In the use example below I spawn two processes from the simple.erl module pasted above and send them different messages.


1> c(simple).
{ok,simple}
2> P1 = simple:start(0).
<0.72.0>: X is 0
<0.72.0>
3> P2 = simple:start(100).
<0.74.0>: X is 100
<0.74.0>
4> simple:sub(P1, 10).
<0.72.0>: X is -10
ok
5> simple:sub(P2, 10).
<0.74.0>: X is 90
ok
6> simple:add(P1, 1000).
<0.72.0>: X is 990
ok
7> simple:add(P1, 50).
<0.72.0>: X is 1040
ok
8> simple:sub(P2, 1000).
<0.74.0>: X is -910
ok


Think carefully about the above code and why it works the way it does.

For a more fully fleshed-out (but still minimal) OTP example consider the Example Server code.
https://gitlab.com/zxq9/example-server/tree/master

es_client.erl
https://gitlab.com/zxq9/example-server/blob/master/src/es_client.erl
Very similar to the above code -- it is a OTP compliant process that handles a socket and receives messages from other chatters.

es_client_man.erl
https://gitlab.com/zxq9/example-server/blob/master/src/es_client_man.erl
Also similar to the above code, but is written as a gen_server for convenience. You don't see the loop/3 function -- it is hidden higher up in gen_server code -- what you do see are the callback functions that handle incoming messages: handle_call/3, handle_cast/2, and handle_info/2.

I recommend playing around with the simple.erl a bit and rewriting it to do whatever you want first.
Then clone the Example Server repository (it can be launched with the ./start script, open a telnet port with es:listen(PortNumber) is how you make it listen) and edit that to make it do whatever you want on the network.

With Erlang you just have to write a bunch of little programs and mess around editing existing programs until it all suddenly clicks!


-Craig



More information about the erlang-questions mailing list