What do you think of this little helper to remove some mess on callbacks?

I have a tiny module defining some macros that allows me to define a GenServer callbacks like this:

  use GenServer

  # This matches when `[name: name]` is passed as argument
  oninit [name: name] do
    # `name` is available
  end

  # This matches everything that does not match above clause
  oninit do
    # A variable called `args` is available
  end

  # Match event, from and state parameters
  oncall someevent, from: {somepid, _ref}, state: %{somvar: somevalue} do
    # `somevalue` and `somepid` are available
    # `event`, `from` and `socket` are available too
  end

  # Every parameter to `oncall` is optional.
  # This matches evey call not matched by above clause
  oncall do
    # `event`, `from` and `socket` are available too
  end

  # `oncast` and `oninfo` are just the same as `oncall`, just `from` is not available
  oncast someevent, state: %{key: value} do
    # `value` is available
    # `event` and `socket` are available too
  end

  oncast do
    # `event` and `socket` are available too
  end

  # Match only on event
  oninfo someevent do

  end

Basically, allows to not write the parameters that you don’t care about, and write callbacks more easily.

What are your takes on this? Do you like this, or you like the more explicit approach of writing everything?

If I see that this will be useful to people, I would expand it to include macros for other OTP behaviors, as well as Phoenix behaviors like LiveView or Channel.

In any circumstances, I will write another helper for myself for LiveView, because my LiveView callbacks tend to get kind of messy.

EDIT:

I forgot to mention that for callbacks implemented this way, there is no need to always return {:ok, state}, {:noreply, state} or {:reply, reply, state}.

I you return something that does not match a standard for that callback, it will be wrapped in tuple most appropriate for that callback, e.g. return just a map and oninfo will convert it to {:noreply, state}.

Personally, I much prefer the explicitness. There is some value in being able to jump to a different project and have a certain familiarity with what’s going on. This one is more debatable, I think: if there is a param that you use 10% of the time, is it better to “hide” it (convenience, easier to learn) or is it better to expose it (explicitness, discoverability)? I’d lean towards exposing it.

I’m not a fan of this. It invites some level of laziness, magic, potentially - bugs.

3 Likes