如何创建和使用自定义Erlang行为?

时间:2015-09-01 16:41:13

标签: erlang

尝试在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项目(虽然可能会更努力)。

1 个答案:

答案 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

希望这个小小的演练为这条道路提供了一些启示。