fireproofsocks
Why are behaviour methods referred to as "callbacks"?
Coming from an OO background, my first reaction is to think of behaviours as interfaces. So it makes sense that a module must define one or more functions outlined in a behaviour if it wishes to implement that behaviour.
So why in Elixir are the behaviour’s functions referred to as “callbacks”? In other languages, a “callback” has quite different connotations which have little to do with inheritance.
In fact, the page at https://elixir-lang.org/getting-started/typespecs-and-behaviours.html has left me scratching my head, especially the section on “Dynamic dispatch”. Since “callbacks” are apparently not at all what you might think, it makes me really wonder what exactly is meant by “dynamic dispatch”. There are no clarification or examples in that section (!!!), only a reassurance that “you don’t need to define a behaviour in order to dynamically dispatch on a module, but those features often go hand in hand.”
Can someone help shed light on this? Thank you!
Most Liked
peerreynders
In simple terms the “behaviour” already exists as the behaviour module capturing the generic parts. You are implementing the callback module which contains the specific parts. To execute those specific parts the behaviour module calls the “callback” functions on the callback module.
The behaviour module is passed a callback module much in the same way a function is passed a callback function - the difference is that the behaviour module expects certain functions to be implemented by the callback module rather than there just being that one function.
defmodule Parser do
@callback parse(String.t) :: {:ok, term} | {:error, String.t}
@callback extensions() :: [String.t]
def parse!(callback_module, contents) do
case callback_module.parse(contents) do
{:ok, data} -> data
{:error, error} -> raise ArgumentError, "parsing error: #{error}"
end
end
end
I swapped implementation with callback_module - so it makes sense that callback_module.parse/1 is a callback function.
Another example from the Access behaviour
def fetch(%module{} = container, key) do
module.fetch(container, key)
rescue
exception in UndefinedFunctionError ->
raise_undefined_behaviour(exception, module, {^module, :fetch, [^container, ^key], _})
end
The fetch/2 function implemented by Access grabs the callback module from the struct so it can turn around and call the module.fetch/2 callback function.
With behaviours you are composing the callback module with the behaviour module - there is no inheritance.
peerreynders
Thought experiment - referring to the Frequency callback module from my last post
def start,
do: Server.start(__MODULE__, [])
That sample code (server.exs) can be easily rewritten to work in the following fashion:
def start,
do: Server.start(
&__MODULE__.init/1,
&__MODULE__.handle/2,
&__MODULE__.terminate/1,
[]
)
Are init/1, handle/2, terminate/1 callbacks now?
Wouldn’t developers say: Why can’t I just pass the module that implements those functions?
peerreynders
You have not yet used GenServers? They do it very similar to this…
Just to underline this point: example
The Demo module is the callback module. None of the usual behaviour ceremony is actually necessary - other than implementing the mandatory callback functions.
{:ok, pid} = GenServer.start_link(Demo,[])
The Demo callback module is composed with the GenServer behaviour module leading to a GenServer based process with the specialized Demo functionality.
In OO:
parse!/1would be a method on the abstract class. It may have some generic logic but doesn’t do the actual work. It would call a hook methoddo_parse/1that is left for the subclass to implement the content specific parsing.- The subclass overides
do_parse/1implementing the content specific implementation. You use the subclass to parse the content it is specialized for.
So:
Parser.parse!/2is the generic function in the behaviour module that will delegate the details of parsing the specific content toMyParser.MyParseris a collection of hook function implementations needed to parse the specific content (organized as a callback module).







