如何创建和使用自定义 Erlang 行为?
How to create and use a custom Erlang behavior?
试图在 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 项目进行 grep(虽然可以更努力)。
你们很亲近。它实际上比您尝试的更容易:
-module(my_behavior).
-callback fn(A :: term()) -> B :: term().
编译器可以完全理解这一点,就这样。
太简单了,有点虎头蛇尾。
编辑
"Cool story, how to use?"
没有什么能比得上工作示例了:
这里我们有一个抽象服务。它应该对非常狭窄的信息范围做出回应,并嘲笑其他任何东西。不过,它的特殊之处在于它接受一个模块的名称作为启动参数,该模块定义了其行为的一些特殊方面——这就是回调模块。
-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
中定义的任何称为 my_behavior
的东西):
11> my_other_callbacks:haha_wtf(5).
4
希望这个小小的演练能为这条道路指明方向。
试图在 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 项目进行 grep(虽然可以更努力)。
你们很亲近。它实际上比您尝试的更容易:
-module(my_behavior).
-callback fn(A :: term()) -> B :: term().
编译器可以完全理解这一点,就这样。
太简单了,有点虎头蛇尾。
编辑
"Cool story, how to use?"
没有什么能比得上工作示例了:
这里我们有一个抽象服务。它应该对非常狭窄的信息范围做出回应,并嘲笑其他任何东西。不过,它的特殊之处在于它接受一个模块的名称作为启动参数,该模块定义了其行为的一些特殊方面——这就是回调模块。
-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
中定义的任何称为 my_behavior
的东西):
11> my_other_callbacks:haha_wtf(5).
4
希望这个小小的演练能为这条道路指明方向。