[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