尝试在Erlang中定义自定义行为,我无法找到在行为定义模块中应用回调函数的方法。编译器声称,回调函数未定义。
我期望行为中的回调函数就像OO语言中的抽象方法一样,可以在不指定实现的情况下使用该方法。
以下示例定义了一个回调函数fn。然后,该函数在add_one中使用。 fn实际上做的是由实现此行为的Erlang模块控制的。
-module( mybeh ).
-callback fn( A::number() ) -> B::number().
-export( [add_one/1] ).
add_one( A ) ->
1+fn( A ).
但是当我尝试编译文件mybeh.erl时,我收到以下错误消息:
$ erlc mybeh.erl
mybeh.erl:8: function fn/1 undefined
我在erlangcentral.org,learnyousomeerlang.com或metajack.im上找到的代码示例过于简单,无法涵盖此案例。我也没有运气通过Github上众所周知的Erlang项目(虽然可能会更努力)。
答案 0 :(得分:10)
你非常接近。它实际上比你尝试的更容易:
-module(my_behavior).
-callback fn(A :: term()) -> B :: term().
编译器可以完全理解这一点,就像它一样。
这么容易,它有点虎头蛇尾。
修改强>
“酷的故事,怎么用?”
没有什么能像一个工作的例子:
这里我们有一个抽象的服务。它应该响应非常狭窄的信息,并嘲笑其他任何事情。但它的特殊元素是,它接受一个模块的名称作为其启动参数,该模块定义了其行为的一些特殊方面 - 这就是回调模块。
-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.
所以在这里我们根据上面定义为'my_behavior'
的行为定义一个非常简单的回调模块:
-module(my_callbacks).
-behavior(my_behavior).
-export([fn/1]).
fn(A) -> A + 1.
这是在行动!
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
那么行为定义有什么用呢?它实际上并没有做任何东西!嗯,Dialyzer类型的层次结构声明有什么用呢?他们也没有做任何事情。但是它们可以帮助你自动检查你的工作,以确保你不会遇到一些令人兴奋的运行时失败 - 但Dialyzer和行为定义都没有强迫你做任何事情:它们只是警告我们(可能)即将到来的厄运:
-module(my_other_callbacks).
-behavior(my_behavior).
-export([haha_wtf/1]).
haha_wtf(A) -> A - 1.
当我们建立这个时,我们得到:
10> c(my_other_callbacks).
my_other_callbacks.erl:2: Warning: undefined callback function fn/1 (behaviour 'my_behavior')
{ok,my_other_callbacks}
但是请注意,此模块 实际上是已编译的,并且仍可独立使用(但不是我们的抽象服务,它希望在名为{{{{{}的任何内容中定义fn/1
。 1}}):
my_behavior
希望这个小小的演练为这条道路提供了一些启示。