Search code examples
callbackerlangerlang-otp

Are Erlang -callbacks to be invoked only through MFA functions (apply/3, spawn/3, ...)? (Custom Behaviors HOWTO)


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.


Solution

  • 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}