'Erlang: Understanding Gen_server'
Gen_server is a great way to create simple servers without having to write a lot of code at all. Here’s a brief overview to get you started.
For some reason, figuring out how the gen_server behavior works in erlang was kind of a pain for me. I think it’s because I can’t just implement something, I need to know why it works. Well, now I know.
So, lets get started. Gen_server is built into erlang. It abstracts the message passing away. As far as I can tell, yes, you could write this functionality yourself. But why would you want to when it’s done for you?
For starters, there’s a few functions you need to implement. If you’re using Erlide, you can create a new module and choose gen_server as the behavior, and it’ll give you a skeleton. Nifty. I strongly recommend going this route, as it has a lot of helpful comments in the code.
Lets write an addition and subtraction server. It’s really not that useful, but it’s enough to get started and understand what’s happening.
Init
%% ——————————————————————– %% Function: init/1 %% Description: Initiates the server %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% ——————————————————————– init([]) -> {ok, 0}.
Here’s a very important thing to understand. A gen_server maintains a state. The State that’s returned above is the starting state of the server. In our case, the initial state will be zero.
handle_call Now we want to add some functionality. Lets add addition and subtraction. We’ll need to export our new functions as well, so lets make sure we put this in with the exports:
-export([add/1, start/0, subtract/1]).
And the actual code:
add(Num) -> gen_server:call( ?MODULE, {add, Num}). subtract(Num) -> gen_server:call( ?MODULE, {subtract, Num}).
handle_call({add, Num}, _From, State) -> {reply, State + Num, State + Num}; handle_call({subtract, Num}, _From, State) -> {reply, State - Num, State - Num};
When you call counting:add, it will in turn call the gen_server:call function, which takes a module and a parameter. It will then call “handle_call” with this paramater, the Pid it came from, and the State of the server. The parameter can be anything, so if we use a tuple, we can take advantage of erlang’s pattern matching. handle_call will return what the gen_server should do next - reply (return a result), noreply, or stop. In this case, we’re replying.
The second parameter is the reply. We’re just going to return the current count here.
We also need to return the new server state in parameter 3. I chose to put the new state in param 2 because it’s useful, but we could just as easily put anything there - an atom, a random string, whatever.
There’s a lot of options within there, so you might want to check the docs.
I STRONGLY recommend using Erlide for your development. It auto compiles and gives you an interactive shell right in the editor. It made Erlang development so much easier. It’ll also create the stubs for the other functions you’ll need (terminate, code change, handle_info, handle_cast).
FYI, If you get an error like this:
** exception exit: {noproc,{gen_server,call,[calculator,{add,1}]}} in function gen_server:call/2
It’s because your gen_server isn’t actually running. Make sure you start it before you test with
calculator:start().
Hopefully this is enough to get you started. Read up more in the official Erlang docs for gen_server. Scroll to the bottom to find the specifications on what functions you must implement, and what they need to return.
Enjoy!
If you found this post helpful, please consider sharing to your network. I'm also available to help you be successful with your distributed systems! Please reach out if you're interested in working with me, and I'll be happy to schedule a free one-hour consultation.