Должны ли Erlang -callbacks вызываться только через функции MFA (apply / 3, spawn / 3, ...)? (Пользовательское поведение HOWTO) - PullRequest
0 голосов
/ 14 ноября 2018

Это мое подозрение, так как этот простой код

-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

Если это так, то в других местах это явно не комментируется: официальные документы , "выучить эрланг" , этот ответ .

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

HOWTO Erlang Custom Behaviors (шаблонный шаблон).

-1. Написать общий модуль. Здесь определен алгоритм, но некоторые шаги (функции обратного вызова в номенклатуре Эрланга) оставлены для будущего конкретного определения.

%% 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: Предупреждение: универсальное поведение не определено")

-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"
0 голосов
/ 14 ноября 2018

Необходимо указать имя модуля обратного вызова, что можно сделать с помощью 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}
...