Erlang Central

Defining Your Own Behaviour

From ErlangCentral Wiki

Contents

Why Define a Behaviour?

In general behaviours are just a convenience for the coder. They insure that a module has implemented all the functions it needs to interact successfully with the system that has defined the behavior. When you are creating a library or application that will be made use of by other libraries or applications with associated callbacks it may be a good idea to define a behaviour.

Defining the Behaviour

Behaviours are pretty simple to define, although the details vary due to a change with R15B.

R14 and earlier

In a module with the desired behaviour name, define and export a function called behaviour_info with a single argument that is the atom callbacks.

The function should return a list of tuples that specify the required callback functions and their arity. It should look something like:


-module(some_behaviour).

-export([behaviour_info/1]).

behaviour_info(callbacks) ->
    [{init,1},
     {handle, 1},
     {sync, 2}];
behaviour_info(_Other) ->
    undefined.

This behaviour requires the functions init/1, handle/1, and sync/2 to be defined in the callback module.

Note that, although Erlang recognizes -behavior() as an American alternative to -behaviour() in the module that applies the behaviour (see Usage below), this function must be named behaviour_info, not behavior_info.

R15 and later

As of R15B, the compiler supports an alternative mechanism for defining the interface for a behaviour that provides information that Dialyzer can leverage.

Instead of defining and exporting behaviour_info/1 in the eponymous module, use the new -callback directive to indicate not only the name of the function, but also the types of its arguments and its expected return values.

The same example as above, with types added:


-module(some_behaviour).

-callback init(Args :: list(term())) -> 'ok'|tuple('error', Reason :: string()).

-callback handle(Event :: atom()) -> NextEvent :: atom().

-callback sync(Node :: node(), Timeout :: non_neg_integer()) -> 'ok'|tuple('error', Reason :: string()).

This will compile down to the same behaviour_info/1 function as before.

Usage


-module(usage).

-behaviour(some_behaviour).

-export([init/1, handle/1, sync/2, foo/0]).

init(Config) ->
    Config.

sync(_Entry, Config) ->
    Config.

handle(Message) ->
    Message.

foo() ->
    foo_atom_returned.

Remember to compile the behaviour module before usage. The compiler will give a warning if any of the callback functions aren't defined.