Reputation: 447
I'm new in functional approach. For my learning I choose two functional languages, Erlang and Clojure. I'm trying to write simple socket server which is able to store information about current connections.
And if, for example, socket1 wants to send message to the socket2 it should send to the server:
"message" to socket2.
Or it can be JSON. Message format is doesn't matter right now.
Using Clojure I found some mutable data structures such as Agent. In Clojure's case I'm using Agent as mutable array where I'm storing all current connections. But I can't find anything similar in Erlang. Could you please help me with it.
I googled my question but I found that there are no mutable data structures in Erlang. Where, in this case, and how I can store all current connections?
Thank you.
Upvotes: 2
Views: 2095
Reputation: 386
Before you dive into ets tables, it's worth taking your time to implement something like an ets table by yourself. The idea is that you start a process, and the state of this process is your stored data.
Below is a simple example of a storage-cell-type of process, which can hold on to a value indefinitely, and also allow users to overwrite this value. This can be easily extended to hold a dictionary in the state, allowing for Key+Value operations.
-module(simple_storage_cell).
-export([start/0, get/1, put/2]).
start() ->
spawn_link(fun() -> loop([]) end).
get(Pid) ->
Pid ! {get, self()}, %% the self() here gets the pid of the
%% calling process, not the storage cell
receive {retrieved, Val} -> Val
after 500 -> error(timeout)
end.
put(Pid, Val) ->
Pid ! {put, self(), Val}, %% see above, in get/1
receive {saved, Val} -> Val
after 500 -> error(timeout)
end.
loop(StoredItem) ->
receive
{get, RequestingPid} ->
RequestingPid ! {retrieved, StoredItem},
%% we now 'hold' the StoredItem in the cell
loop(StoredItem);
{put, RequestingPid, NewValue} ->
RequestingPid ! {saved, NewValue},
%% we lose the old value, keeping NewValue in the cell
loop(NewValue);
OtherMsg -> error({unexpected_msg, OtherMsg})
end.
This gives you the following behaviour:
1> Cell = simple_storage_cell:start().
<0.33.0>
2> simple_storage_cell:get(Cell).
[]
3> simple_storage_cell:put(Cell, "hello").
"hello"
4> simple_storage_cell:get(Cell).
"hello"
5> simple_storage_cell:get(Cell).
"hello"
6> simple_storage_cell:put(Cell, "new value").
"new value"
7> simple_storage_cell:get(Cell).
"new value"
Please be aware that this kind of programming essentially re-introduces global state into a (nominally) stateless system. If you have two processes get
ting and put
tting values into one cell, you've just introduced resource contention and lost many of the benefits functional programming is supposed to give you.
If I may add a personal note: it would be wiser to pick a language with strict typing discipline as your second
functional language. Rather than learning Clojure+Erlang, I'd suggest combinations like:
(Clojure, Scala), (Erlang, Haskell), (Scheme, *ML)
which are more likely to enrich your intellectual toolbox.
Upvotes: 6
Reputation: 20014
You might consider using an Erlang Term Storage (ets) table. It's an in-memory key-value store that allows for replacing existing entries with new entries.
Upvotes: 4