GenServer documentation questions

I have some questions about the GenServer documentation and things I can’t find in it:

The docs say that all 6 callbacks are automatically defined for you but the default code is only given for handle_call/3, handle_cast/2 and handle_info/2. What are the others and where are they defined?

In the example code for the Stack module there are calls to local functions (macros?) super/2 and super/3 but these are not defined or explained anywhere. What do these do?

2 Likes

They are defined in GenServer.using/1. I believe these are the ones which interest you:

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

def terminate(_reason, _state) do
  :ok
end

def code_change(_old, state, _extra) do
  {:ok, state}
end

super is not a local, it’s a feature of Elixir that allows overriding functions https://hexdocs.pm/elixir/Kernel.SpecialForms.html#super/1. I’m not a big fan, but that’s what we have right now - I guess it would look differently, if we had optional callbacks at the time this was defined.

When I can’t find something in the GenServer documentation, I look in the gen_server documentation :stuck_out_tongue:

In this case it does not help as in Erlang we don’t generally automatically define functions for you.

1 Like

My point was that these 3 are not documented. :grinning: Me, as poor user, should not have to go look in the code. And @michalmuskala this only sort of helps me as I don’t know what the default values are.

I am not personally not a big fan of having the system define default functions for me as I like everything to be explicit. Maybe more code, in this case very little, but it is explicit and leaves very little to doubt which is a big win when maintaining code.

2 Likes

[quote=“rvirding, post:7, topic:10441”]
My point was that these 3 are not documented.
[/quote]

I’m not disagreeing that this is a bug in docs, I was merely giving you the answer to your question :slight_smile:

You could always write @behaviour GenServer instead. Perhaps this should be documented as well.

2 Likes

Hopefully Elixir 1.6 will actually warn in case init/1 is not defined an the warning informs about what default is introduced. I agree the others should be properly documented.

Please open issues about the documentation in the issue tracker [1] since it’s easy for contributors to miss issues reported on the forum.

[1] https://github.com/elixir-lang/elixir/issues

2 Likes

Does this mean that the prevailing opinion is that I should define an init/1 even if my GenServer doesn’t hold state?

If you want to be nit-picky a GenServer always holds state even if you choose not to use it. The init/1 callback should return {:ok,data} and this data is then then threaded through all calls to handle_call/3, handle_cast/2 and handle_info/2 to be finally consumed in terminate/2. It is always there even if you choose not to use it so setting it to some value even if it is nil seems pretty logical.

4 Likes

And is there any overhead to using gen_servers compared to other processes? I ask because it probably became my favourite abstraction and I now tend to look at most problems through a “does a genserver fit in solving this”, but I also question if it brings with it any relevant overhead? I found an erlang mailing list discussion where someone mentioned they don’t. At first when I read about them, perhaps due to the naming (generic server), it always felt like they were supposed to hold “heavier” duties, but I think they fit a lot of lighter problems (if having the mailbox, constant state, time-out control and message handling helps doing the lighter flow)

It’s truly ingenious and I really like that it’s so versatile, you can use to manage centralised state requirements and to manage individual process level state, write them completely asynchronously while keeping execution inside the asynchronous calls synchronous (or asynchronous)…

Good point. I forgot that it just takes my nil arg and keeps it around.
But I meant more, if there’s going to be a warning in the future, have I been doing it wrong by just throwing in a nil when using GenServer as a process abstraction instead of a state abstraction?

GenServer’s handle system messages for you and all, and there is the tiny slight overhead of the module call syntax, but nothing you’ll care about in 99.9999999% of cases. It is fine to think of it as the ‘basic’ process type. :slight_smile:

2 Likes

I like the detail of answer having 9 nines…

/me coughs ^.^