How to create and use Erlang custom behavior?

Trying to define custom behavior in Erlang, I cannot find a way to apply the callback function inside the behavior definition module. The compiler claims that the callback function is undefined.

I expected that the callback function in the behavior would work just like an abstract OO method, where you can use the method without specifying an implementation.

The example below defines the callback function fn. This function is then used in add_one. In fact, fn is actually controlled by the Erlang module that implements this behavior.

-module( mybeh ). -callback fn( A::number() ) -> B::number(). -export( [add_one/1] ). add_one( A ) -> 1+fn( A ). 

But when I try to compile mybeh.erl file, I get the following error message:

 $ erlc mybeh.erl mybeh.erl:8: function fn/1 undefined 

The code examples I found on erlangcentral.org , learnyousomeerlang.com, or metajack.im were too simplistic to cover this case. Also, Iโ€™m not lucky that I went through the well-known Erlang projects on Github (maybe he tried hard).

+7
source share
1 answer

You are very close. Actually it is easier than you tried:

 -module(my_behavior). -callback fn(A :: term()) -> B :: term(). 

The compiler can fully understand this as it is.

So easy, it's kind of anti-climatic.

EDIT

"Cool story, how to use?"

Nothing says as a working example:

Here we have an abstract service. It should respond to a very narrow range of messages and make fun of anything else. Its special element, however, is that it takes as a start argument the name of the module that defines some particular aspect of its behavior - and it is a callback module.

 -module(my_abstract). -export([start/1]). start(CallbackMod)-> spawn(fun() -> loop(CallbackMod) end). loop(CBM) -> receive {Sender, {do_it, A}} -> Sender ! CBM:fn(A), loop(CBM); stop -> io:format("~p (~p): Farewell!~n", [self(), ?MODULE]); Message -> io:format("~p (~p): Received silliness: ~tp~n", [self(), ?MODULE, Message]), loop(CBM) end. 

So, here we define a really simple callback module according to the behavior defined as 'my_behavior' above:

 -module(my_callbacks). -behavior(my_behavior). -export([fn/1]). fn(A) -> A + 1. 

Here it is in action!

 1> c(my_behavior). {ok,my_behavior} 2> c(my_abstract). {ok,my_abstract} 3> c(my_callbacks). {ok,my_callbacks} 4> Service = my_abstract:start(my_callbacks). <0.50.0> 5> Service ! {self(), {do_it, 5}}. {<0.33.0>,{do_it,5}} 6> flush(). Shell got 6 ok 7> Service ! {self(), {do_it, 41}}. {<0.33.0>,{do_it,41}} 8> flush(). Shell got 42 ok 9> Service ! stop. <0.50.0> (my_abstract): Farewell! stop 

So what's good about defining behavior? It really didn't do anything! What good is these Dialyzer type hierarchy declarations? They donโ€™t do anything either. But they help you automatically check your work to make sure that you donโ€™t encounter some exciting error at runtime - but neither Dialyzer, nor the definition of behavior forces you to do anything: they just warn us about our (probable) impending doom:

 -module(my_other_callbacks). -behavior(my_behavior). -export([haha_wtf/1]). haha_wtf(A) -> A - 1. 

And when we build it, we get:

 10> c(my_other_callbacks). my_other_callbacks.erl:2: Warning: undefined callback function fn/1 (behaviour 'my_behavior') {ok,my_other_callbacks} 

Please note, however, that this module was actually compiled and can still be used independently (but not by our abstract service, which expects to find fn/1 defined in everything called my_behavior ):

 11> my_other_callbacks:haha_wtf(5). 4 

Hopefully this little walkthrough sheds light on the track.

+9
source

All Articles