仅通过MFA函数(apply / 3,spawn / 3,...)才能调用Erlang回调吗? (自定义行为指南)

时间:2018-11-14 09:35:06

标签: callback erlang otp

这就是我对这个简单代码的怀疑

-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) ]) .

无法编译:

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

如果是这种情况,则在其他地方未明确评论: official docs"learn erlang"this answer

2 个答案:

答案 0 :(得分:1)

您需要提供回调模块的名称,可以通过applyspawn完成,但是您也可以使用变量作为模块名称来进行简单调用,例如CallbackModule:say(1)

因此您可以执行以下任一操作:

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

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

这两个版本是等效的。

让我们创建一个实现simple_server行为的回调模块:

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

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

现在我们可以使用模块名称作为参数来调用simple_server:sayHello

> simple_server:sayHello(my_callback).
Hello 1: {1,is,the,loneliest,number}
Hello 2: {2,is,the,loneliest,number}

答案 1 :(得分:1)

HOWTO Erlang自定义行为(模板方法模式)。

-1。编写一个通用模块。这里定义了一种算法,但是还留了一些步骤(以Erlang命名的回调函数),以供将来使用。

%% 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。编译generic.erl

erlc generic.erl

-(3。)尝试编写提供程序(回调)模块

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

-(4。)编译callbacks1.erl:使用-pa 表示generic.beam的位置。 (因此,发出了关于say / 1的预期警告。)

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

(注意:如果未指定-pa,您将得到以下信息:“ callbacks1.erl:2:警告:行为通用未定义”)

-3。编写正确的提供程序(回调)模块。

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

-export( [say/1] ).

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

-4。编译

erlc -pa . callbacks2.erl

(现在确定)。

-5。编写main.erl以使用回调模块收集通用模块。

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

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

-6。编译并运行main

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

我们得到:

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