0
votes

I am new to Elixir and was reading through a book and doing some examples. Here is the piece of code that makes me ask question here:

defmodule Sequence.Server do
    use GenServer
    def init(initial_number)do 
        {:ok,initial_number}
    end

    def handle_call(:next_number, _from, current_number)do
        {:reply, current_number,current_number+1}    
    end 
end

As I know the init function is called when the server is being initialized and we are defining some parameter - which will be the initial state of the server. The thing that makes me confused is that how current_number and the initial_number are related to each other, I mean nowhere in the code we are saying something like that

current_number = initial_number

Because when I call GenServer.call(some_process_id, :next_number) it starts from 100 for example if the parameter to start_link was 100. How does Elixir understands that it must start from 100 when we dont have any mapping between the initial state and the current_number parameters

2
I would like to suggest the great book of LYSE, specifically chapters learnyousomeerlang.com/more-on-multiprocessing and learnyousomeerlang.com/what-is-otp and learnyousomeerlang.com/clients-and-servers in order to understand how a gen_server (and other gen_* abstractions) are written. Reading whole book is highly recommended, and it's fun too :) - vfsoraki
Thank you all :) - Tano

2 Answers

7
votes

The purpose of an init/1 is to setup up internal state of a GenServer. The only way to amend this GenServer's internal state, is via call-ing, cast-ing, or sending regular messages (then, being handled by handle_info/2 callback).

Those functions (respectively, handle_call/3 and handle_cast/2) will be invoked with the GenServer's internal state passed in as the last argument of a function's argument.

Consider the scenario:

  1. You're initialising the GenServer with number 100.
  2. You send a message (a call to be more specific) to this GenServer - :next_number.
  3. This invokes your handle_call(:next_number, _from, current_number) callback function, where current_number has the initial value of 100.
  4. As a return value from this function, you specified the tuple: {:reply, current_number,current_number+1}, which you should understand like: reply (first element of a tuple) indicates it will reply to the caller; the second element of the tuple will be the value returned to caller (in this case, it will be number 100); and the last element of the tuple will be the new state of a GenServer - current_number+1, so 101.
  5. When you send another :next_number call to this GenServer, it will follow the previous steps, except, the internal state will now be 101, and after returning from the function, the new state will be 102, etc...
1
votes

The result of init is {:ok, initial_state}, where the state value is held by GenServer.

The signature of handle_call is actually handle_call(request, from, current_state), and it returns {:reply, result, new_state}.

This means that when you do GenServer.call(pid, :next_number), that results in a call to handle_call(:next_number, _from, state), where the state -- passed as current_number -- starts as initial_number and the result of handle_call saves a new state with value current_number.

The next time you call GenServer.call(pid, :next_number), it's called with the new state, and you return the new-new state, and so on...