你很亲密。它实际上比您尝试的要容易:
-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}
但请注意,这个模块已经实际编译过并且仍然可以独立使用(但不是由我们的抽象服务,它期望找到在任何称为 my_behavior 的任何东西中定义的 fn/1) :
11> my_other_callbacks:haha_wtf(5).
4
希望这个简短的演练能对这条道路有所启发。