0

That's my suspicion as this simple code

-module(simple_server).
-export( [sayHello/0] ).

-callback say(Num :: term()) -> term().

sayHello() ->
    io:fwrite( "Hello 1: ~p\n", [ say(1) ]) ,
    io:fwrite( "Hello 2: ~p\n", [ say(2) ]) .

fails to be compiled:

$ erlc simple_server.erl
simple_server.erl:7: function say/1 undefined
simple_server.erl:8: function say/1 undefined

If that is the case, then this is not explicitly commented elsewhere: official docs, "learn erlang", this answer.

4
  • This question stackoverflow.com/questions/32336854/… exposes the same problem without clearly showing how callbacks are supposed to be called. Commented Nov 14, 2018 at 9:58
  • In the accepted answer, the callback function is called as CBM:fn(A). Commented Nov 14, 2018 at 9:59
  • @legoscia Yes. Then, the name of the module implementing the callback must be supplied to the generic code. Commented Nov 14, 2018 at 10:02
  • @legoscia If you are sure about the ways a generic module can call a callback (to be provided in the future) you could write an answer here to be accepted. Thanks in advance. Commented Nov 14, 2018 at 10:06

2 Answers 2

2

You need to provide the name of the callback module, which can be done through apply and spawn, but you can also make a simple call using a variable as the module name, e.g. CallbackModule:say(1).

So you could do either:

sayHello(CallbackModule) ->
    io:fwrite( "Hello 1: ~p\n", [ CallbackModule:say(1) ]) ,
    io:fwrite( "Hello 2: ~p\n", [ CallbackModule:say(2) ]) .

or

sayHello(CallbackModule) ->
    io:fwrite( "Hello 1: ~p\n", [ apply(CallbackModule, say, [1]) ]) ,
    io:fwrite( "Hello 2: ~p\n", [ apply(CallbackModule, say, [2]) ]) .

Those two versions are equivalent.

Let's create a callback module implementing the simple_server behaviour:

-module(my_callback).
-behaviour(simple_server).
-export([say/1]).

say(N) ->
    {N, is, the, loneliest, number}.

Now we can call simple_server:sayHello with the module name as the argument:

> simple_server:sayHello(my_callback).
Hello 1: {1,is,the,loneliest,number}
Hello 2: {2,is,the,loneliest,number}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot! I've also prepared a simple explanatory example, including compile orders, which are very important (-pa flag).
1

HOWTO Erlang Custom Behaviors (template method pattern).

-1. Write a generic module. Here, an algorithm is defined, but some steps (callback functions in Erlang nomenclature) are left for a future specific definition.

%% generic.erl   
-module(generic).
-export( [sayHello/1] ).

-callback say(Num :: term()) -> term(). %% future definition

%% generic algorithm: needs the reference to the provider module                                 
sayHello(ProviderModule) ->
  io:fwrite( "Hello 1: ~p\n", [ ProviderModule:say(1) ]) ,
  io:fwrite( "Hello 2: ~p\n", [ ProviderModule:say(2) ]) .

-2. Compile generic.erl

erlc generic.erl

-(3.) Try to write a provider (callback) module

%% callbacks1.erl (fails to implement say/1 callback)
-module( callbacks1 ).
-behaviour( generic ).

-(4.) Compile callbacks1.erl: use -pa . to say where generic.beam is. (Therefore, the expected warning about say/1 is issued).

erlc -pa . callbacks1.erl
callbacks1.erl:2: Warning: undefined callback function say/1 (behaviour 'generic')

(Note: If -pa is not given, you'll got this: "callbacks1.erl:2: Warning: behaviour generic undefined")

-3. Write a correct provider (callback) module.

%% callbacks2.erl
-module( callbacks2 ).
-behaviour( generic ).

-export( [say/1] ).

say(1) -> "good morning";
say(2) -> "bon jour";
say(_) -> "hi".

-4. Compile it

erlc -pa . callbacks2.erl

(Ok now).

-5. Write a main.erl to gather generic module with callback module.

%% main.erl
-module( main ).
-export( [main/0] ).

main() ->
    %% call the generic algorithm telling it what callback module to use
    generic:sayHello( callbacks2 ) 
    . % ()

-6. Compile and run main

erlc main.erl
erl -noshell -s main main -s init stop

We get:

Hello 1: "good morning"
Hello 2: "bon jour"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.