The example you cite is a header file (.hrl), not a source file (.erl) and they are importing it at the top of files that should be behaving a certain way.
This seems to be a way of enforcing that a handful of modules all behave the same way. Which puzzles me, because this kind of behavioral definition is exactly what behaviors exist to handle.
Also, note that their api.hrl
also imports wf.hrl
and then these are imported all over the place in the project. From what I can tell glancing at that code there really is no reason for that to not have been defined as a behavior. (There may have been a reason, but it is not obvious from glancing at the code... and I can't think of a good reason to avoid using behaviors for this case.)
Function specs
When defining a function spec you need to have an underlying function to annotate with it. So this, all by itself, is illegal:
-spec add(integer(), integer()) -> integer().
But this is legal:
-spec add(integer(), integer()) -> integer().
add(A, B) ->
A + B.
An example of using types, specs, edoc, etc.: https://github.com/zxq9/zuuid
Defining Behaviors
So "What's a behavior?" Let's say you want to have a place in your program where you dynamically choose what module to call out to, and you must expect that module to behave the same way other, similarly defined ones do. If you need to call foo:frob/2
it should work the exact same way (in terms of types) as bar:frob/2
. So we would write a behavior definition somewhere, inside foo_bar.erl
maybe, and then declare our modules foo.erl
and bar.erl
to be behaviors of the type foo_bar
:
-module(foo_bar).
-callback frob(integer(), inet:socket()) -> ok.
And then later...
-module(foo).
-behavior(foo_bar).
frob(A, B) ->
% ...
If module foo
lacks a function frob/2
of the specified type, then a warning will be thrown because it is failing to match the behavior it has declared.
This can be super useful for simplifying the way you check, manage, and keep organized inter-module API compatibility. Also, note that you can define as many callbacks as you want on a module, and you can declare as many behaviors as you want in a module.
See: How to create and use a custom Erlang behavior?