Cavet
Cavet

Reputation: 152

Writing and compiling custom behaviours in Erlang

I'm trying to write and compile a custom behaviour in Erlang.

I cannot find any clear documentation on how to compile this behaviour.

-module(bla).

-export([start_link/0,behaviour_info/1]).

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

%% -callback init(Args :: term()) ->
%%     {ok, State :: term()} | {ok, State :: term(), timeout()} |
%%     {stop, Reason :: term()} | ignore.

start_link()->
    init([]).

my command for comiling is :

erlc.exe .\src\bla.erl

resulting:

bla.erl:24: function init/1 undefined

Anyone an idea on writing and compiling behaviours in erlang, please? any links?

Upvotes: 3

Views: 1159

Answers (1)

Łukasz Ptaszyński
Łukasz Ptaszyński

Reputation: 1689

Defining behaviour callbacks results in an obligation towards your implementation of callback module. In erlang, modules are just function containers, not classes nor interfaces. Behaviours are based on runtime module name resolution Mod:fun(). OTP gen_server (check it) keeps its callback module name after you pass it in: gen_server:start_link(CallbackModuleName, Args, Opts) and here is code for applying callback init/1:

init_it(Starter, Parent, Name0, Mod, Args, Options) ->
    Name = name(Name0),
    Debug = debug_options(Name, Options),
    case catch Mod:init(Args) of
        {ok, State} ->
            proc_lib:init_ack(Starter, {ok, self()}),
            loop(Parent, Name, State, Mod, infinity, Debug);
        {ok, State, Timeout} ->
            proc_lib:init_ack(Starter, {ok, self()}),
            loop(Parent, Name, State, Mod, Timeout, Debug);
        ...

It's applying init/1 of passed callback module kept in Mod parameter, gets its last value, do what you want and keep going (or not, depends on that last value).

Assume we have module bla_impl which looks like this:

-module(bla_impl).
-behaviour(bla).
-export([init/1, start_link/0]).

start_link() -> bla:start_link(?MODULE). %% macro ?MODULE is resolved to bla_impl
init(Args) -> ... .

And now you need to say in bla which module you use by:

-module(bla).
-export([start_link/1]).

start_link(Mod) -> Mod:init([]).

or maybe better solution is to read it from configuration:

-module(bla).
-export([start_link/0]).

start_link() -> 
    Mod = application:get_env(bla_app,  callback_module),
    Mod:init([]),
    ...

There is many ways for doing so.

As you see there is no magic here. This would work even without -behaviour(bla) nor specified callback with -callback. This is just an information for compiler, tools and documentation.

From erlang documentation: Behaviours

And btw. start_link function should spawn another process and link to it.

start_link(Mod) ->
    spawn_link(Mod, init, [[]]).

Upvotes: 2

Related Questions