是否只能通过 MFA 函数(apply/3、spawn/3、...)调用 Erlang 回调? (自定义行为指南)
Are Erlang -callbacks to be invoked only through MFA functions (apply/3, spawn/3, ...)? (Custom Behaviors HOWTO)
这是我的怀疑,因为这个简单的代码
-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", .
您需要提供回调模块的名称,可以通过apply
和spawn
来完成,但您也可以使用变量作为模块名称进行简单的调用,例如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}
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: Warning: behaviour generic undefined")
-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"
这是我的怀疑,因为这个简单的代码
-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",
您需要提供回调模块的名称,可以通过apply
和spawn
来完成,但您也可以使用变量作为模块名称进行简单的调用,例如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}
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: Warning: behaviour generic undefined")
-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"