[erlang-questions] Beginner question

Vance Shipley vances@REDACTED
Wed Mar 16 10:05:09 CET 2016


On Tue, Mar 15, 2016 at 4:23 AM, Pietro <pulsarpietro@REDACTED> wrote:
>
> loop(Table) ->
>     receive
>         {From, {insert, key, value}} ->
>             From ! ets:insert(Table, {key, value}),
>             loop(Table);
>         {From, {lookup, key}} ->
>             From ! ets:lookup(Table, key),
>             loop(Table);
>         Any ->
>             Any
>
>     end.

I recall when beginning with Erlang, many years ago, my first struggle
was with the concept of functional programming.  Where are my global
variables?  Yours is not an example of functional style so I am
compelled to rewrite your loop/1 function in a couple ways to
demonstrate a side effect free functional implementation.

First the simplest case where we have a server manage one value:

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

     loop(Value) ->
         receive
             {From, {set, NewValue}} ->
                 From ! {self(), {ok, NewValue}},
                 loop(NewValue);
             {From, get} ->
                 From ! {self(), {ok, Value}},
                 loop(Value)
         end.

     1> Pid = a:start().
     <0.35.0>
     2> a:interact(Pid, get).
     {ok,undefined}
     3> a:interact(Pid, {set, 42}).
     {ok,42}
     4> a:interact(Pid, get).
     {ok,42}

To handle more values use more arguments (e.g. loop/3):

     start() ->
         spawn (fun() -> loop(undefined, undefined, undefined) end).

     loop(A, B, C) ->
         receive
             {From, {set, a, Value}} ->
                 From ! {self(), {ok, Value}},
                 loop(Value, B, C);
            {From, {set, b, Value}} ->
                 From ! {self(), {ok, Value}},
                 loop(A, Value, C);
            {From, {set, c, Value}} ->
                 From ! {self(), {ok, Value}},
                 loop(A, Value, C);
             {From, {get, a}} ->
                 From ! {self(), {ok, A}},
                 loop(A, B, C);
             {From, {get, b}} ->
                 From ! {self(), {ok, B}},
                 loop(A, B, C);
             {From, {get, c}} ->
                 From ! {self(), {ok, C}},
                 loop(A, B, C);
         end.

To handle many values replace the argument to loop/1 with a data
structure chosen to match our use case:

     start() ->
         spawn (fun() -> loop(dict:new()) end).

     loop(Dict) ->
         receive
             {From, {set, Key, Value}} ->
                 NewDict = dict:store(Key, Value, Dict),
                 From ! {self(), {ok, Value}},
                 loop(NewDict);
             {From, {get, Key}} ->
                 From ! dict:fetch(Key, Dict),
                 loop(Dict)
         end.

... or:

     start() ->
         spawn (fun() -> loop(gb_trees:empty()) end).

     loop(Tree) ->
         receive
             {From, {set, Key, Value}} ->
                 NewTree = gb_trees:enter(Key, Value, Tree),
                 From ! {self(), {ok, Value}},
                 loop(NewTree);
             {From, {get, Key}} ->
                 From ! gb_trees:get(Key, Tree),
                 loop(Tree)
         end.


There's nothing wrong with using the erlang term storage (ets) module.
I just think it's important to understand and embrace the functional
style first or you risk writing terrible imperative code by using ets,
or worse the process dictionary, to accomplish global variables.

-- 
     -Vance



More information about the erlang-questions mailing list