# Chat room using GenServer

It seems odd that state must be passed around all the time. Can't State module hold itself, so I could do something like

state = %State{size: size}
case state.is_full? do
...


Here state is always passed. I suppose this is how it is in FP?

defmodule Chatter.Room do
use GenServer

defmodule State do
defstruct size: 3, players: []

def is_full?(state) do
Enum.count(state.players) >= state.size
end

case State.is_full?(state) do
true -> {:room_is_full, state}
_    -> {:ok, append_player(state, player)}
end
end

defp append_player(state, player) do
%{state | players: state.players ++ [player]}
end
end

def open(size) do
{:ok, pid} = GenServer.start_link(__MODULE__, %State{size: size})
pid
end

def join(room, user) do
GenServer.call(room, {:join, user})
end

def players(room) do
GenServer.call(room, :players)
end

def init(state) do
{:ok, state}
end

def handle_call(:players, _from, state) do
end

def handle_call({:join, player}, _from, state) do
{:ok, state} -> {:reply, :ok, state}
{error, state} -> {:reply, error, state}
end
end
end


Short answer -- yes, data including state is immutable.

In traditional functional programming idiom data is immutable. With this in mind, GenServer stores the current state internally. When you implement the callback functions (handle_call, handle_cast, ...) the current state is passed to your callback function. You then return a new state which then replaces the existing state inside the GenServer. Depending on the data structure used for maintaining your state, only a portion of the new data may be "deeply" copied.

If I relate this directly to your question, I would not implement state as you have. The GenServer wraps the state for you. A more erlang/elixirish outline would be something like:

defmodule ModuleName do
# api functions
# place calls to cast, call, here
def is_full?(server) do
GenServer.call(server, {:check_full})
end

# callbacks
# match ":check_full" call
def handle_call({:check_full}, _from, state) do
# perform your full check actions here
end

def handle_call({:add_user, {user_data}}, _from, state) do
end

# catch all
def handle_call(request, _from, state) do
# do something here, if you want, or omit as suits your style
end
end


Certainly, you can also use guards instead of matching or use one giant handle_call function with a case or cond to separate out the pieces. The method used above is clean and easy to understand.

I may have missed the point of the question --- the short answer was, yes, we pass the state around. The longer answer was the GenServer can keep track of it for you.

• I would like to note that in this example and the question, the state is managed via blocking calls. Cast/handle_cast is non-blocking and is definitely useful in many cases. – uDude Jan 5 '16 at 23:12