Подтвердить что ты не робот

Поведение Erlang/OTP для начинающих

Как я понял из книги "Erlang и OTP in action", поведение слова относится к:

  • интерфейс поведения, который представляет собой набор функций;
  • реализация поведения, которая представляет собой код приложения (модуль обратного вызова);
  • контейнер поведения, который является процессом.

Вопрос:

Что начинающий Erlang/OTP должен знать о поведении? Возможно ли описать и понять понятие поведения OTP в двух словах?

Какая функция обратного вызова действительно означает в контексте Elang/OTP?

Можно ли рассматривать обратные вызовы в реализации поведения, поскольку методы переопределяются в Java?

В книге говорится, что связанная функция обратного вызова для функции библиотеки gen_server: start_link/4 в следующем коде: "Module: init/1".

Означает ли это, что с init/1 мы вызываем библиотечную функцию gen_server: start_link/4? Или это означает что-нибудь еще?

-module(tr_server).

-behaviour(gen_server).

-include_lib("eunit/include/eunit.hrl").

%% API
-export([
         start_link/1,
         start_link/0,
         get_count/0,
         stop/0
         ]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 1055).

-record(state, {port, lsock, request_count = 0}).


%%%===================================================================
%%% API
%%%===================================================================


%%--------------------------------------------------------------------
%% @doc Starts the server.
%%
%% @spec start_link(Port::integer()) -> {ok, Pid}
%% where
%%  Pid = pid()
%% @end
%%--------------------------------------------------------------------
start_link(Port) ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).

%% @spec start_link() -> {ok, Pid}
%% @doc Calls `start_link(Port)' using the default port.
s    tart_link() ->
    start_link(?DEFAULT_PORT).

%%--------------------------------------------------------------------
%% @doc Fetches the number of requests made to this server.
%% @spec get_count() -> {ok, Count}
%% where
%%  Count = integer()
%% @end
%%--------------------------------------------------------------------
get_count() ->
    gen_server:call(?SERVER, get_count).

%%--------------------------------------------------------------------
%% @doc Stops the server.
%% @spec stop() -> ok
%% @end
%%--------------------------------------------------------------------
stop() ->
    gen_server:cast(?SERVER, stop).


%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

init([Port]) ->
    {ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
    {ok, #state{port = Port, lsock = LSock}, 0}.

handle_call(get_count, _From, State) ->
    {reply, {ok, State#state.request_count}, State}.

handle_cast(stop, State) ->
    {stop, normal, State}.

handle_info({tcp, Socket, RawData}, State) ->
    do_rpc(Socket, RawData),
    RequestCount = State#state.request_count,
    {noreply, State#state{request_count = RequestCount + 1}};
handle_info(timeout, #state{lsock = LSock} = State) ->
    {ok, _Sock} = gen_tcp:accept(LSock),
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

do_rpc(Socket, RawData) ->
    try
        {M, F, A} = split_out_mfa(RawData),
        Result = apply(M, F, A),
        gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
    catch
        _Class:Err ->
            gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
    end.

split_out_mfa(RawData) ->
    MFA = re:replace(RawData, "\r\n$", "", [{return, list}]),
    {match, [M, F, A]} =
        re:run(MFA,
               "(.*):(.*)\s*\\((.*)\s*\\)\s*.\s*$",
                   [{capture, [1,2,3], list}, ungreedy]),
    {list_to_atom(M), list_to_atom(F), args_to_terms(A)}.

args_to_terms(RawArgs) ->
    {ok, Toks, _Line} = erl_scan:string("[" ++ RawArgs ++ "]. ", 1),
    {ok, Args} = erl_parse:parse_term(Toks),
    Args.


%% test

start_test() ->
    {ok, _} = tr_server:start_link(1055).
4b9b3361

Ответ 1

Q: Что должен знать начинающий Erlang/OTP о поведении? Это возможно описать и понять понятие поведения ОТП в скорлупа?

Поведение обычно используется в коде, так что компилятор может генерировать более интуитивные сообщения об ошибках в зависимости от его поведения. я application/supervisor/gen_server/gen_event/gen_fsm.

Это позволяет компилятору давать сообщения об ошибках, специфичные для поведения ex: gen_server

Q: Что такое функция обратного вызова, фактически означает в контексте Elang/OTP?

Можно сказать, что функция обратного вызова берется из программирования GUI (по крайней мере, аналогично). Всякий раз, когда происходит событие ex. щелчок мыши - это отдельная функция, которая обрабатывает щелчок мышью.

Таким образом, всякий раз, когда напр. экспортированная функция gen_server вызывается из другого модуля, эта функция может иметь функцию обратного вызова (handle_call/handle_cast), имеющую разные шаблоны.

Q: Можно ли рассматривать обратные вызовы в реализации поведения как методы переопределено в Java?

Да... может быть... нет:)

Q: В книге говорится, что связанная функция обратного вызова для библиотеки function 'gen_server: start_link/4' в следующем коде 'Модуль: инициализации /1'.

gen_server: start_link вызывает функцию init самостоятельно, как ответил w55.... (извините довольно большое имя).


Надеюсь, что я ответил на все ваши запросы:)

Ответ 2

Вместо того, чтобы пытаться решить ваши конкретные вопросы по мере того, как уже были сделаны другие ответы, я попытаюсь объяснить в простых выражениях основы поведения, и позвольте вам ответить на ваши собственные вопросы, основываясь на понимании этих основ.

Поведение в основном представляет собой платформу обработки сообщений, где под "каркасом" я подразумеваю классическое определение частичного решения проблемы, которое может быть выполнено и настроено конечным пользователем. Поведение OTP по существу обеспечивает:

  • цикл сообщений
  • интеграция с базовой поддержкой OTP для обновления кода, отслеживания, системных сообщений и т.д.

Обработка сообщений делегаций делегатов для модулей обратного вызова или реализации поведения, так как их называют "Erlang и OTP In Action". При вызове его функции init/1 модуль обратного вызова обычно создает состояние для цикла сообщений, которое должно оставаться от его имени. Затем цикл поведения передает это состояние каждому последующему вызову функции обработки сообщения модуля обратного вызова, и каждый из этих вызовов может возвращать измененное состояние. Функции обратного вызова также возвращают инструкции, описывающие цикл сообщений о действиях, что делать дальше.

Здесь очень упрощенная версия цикла сообщений в основе поведения:

loop(Callbacks, State) ->
  {Next, NState} =
 receive
                     M1 ->

                       Callbacks:handle_m1(M1,State);
                     M2 ->
                       Callbacks:handle_m2(M2,State);
                     Other ->
                       Callbacks:handle_other(Other,State)
                   end,
  case Next of

    stop -> ok;
    _ -> loop(Callbacks, NState)
  end.

Этот хвосто-рекурсивный цикл имеет в качестве аргументов модуль Callbacks и переменную State. Прежде чем этот цикл будет вызываться первым, вы уже рассказали о поведении вашего модуля обратного вызова, а затем базовый код поддержки поведения OTP уже вызвал вашу функцию обратного вызова init/1, чтобы получить начальное значение State.

Наш примерный цикл поведения принимает сообщения формы M1, M2 и любое другое сообщение, подробности которого здесь не имеют значения, и для каждого сообщения вызывает другую функцию обратного вызова в модуле Callbacks, В этом примере функции обратного вызова handle_m1 и handle_m2 обрабатывают сообщения M1 и M2 соответственно, а обратный вызов handle_other обрабатывает все другие типы сообщений. Обратите внимание, что State передается каждой функции обратного вызова. Ожидается, что каждая функция вернет кортеж с первым элементом, сообщающим циклу, что делать дальше, и вторым элементом, содержащим возможное новое состояние для цикла — либо то же значение, что и State, либо новое другое значение; который цикл хранит в своей переменной NState. В этом примере, если Next является атомом stop, цикл останавливается, но если он что-то еще, цикл вызывает себя рекурсивно, передавая новое состояние NState на следующую итерацию. И так как он хвост рекурсивный, цикл никогда не ударит стек.

Если вы выкапываете источники стандартного поведения OTP, такие как gen_server и gen_fsm, вы найдете такой цикл, похожий на этот, но они намного сложнее из-за обработки системных сообщений, тайм-аутов, трассировки, исключения и т.д. Стандартное поведение также запускает их циклы в отдельном процессе, поэтому они также содержат код для запуска цикла и передачи ему сообщений.

Ответ 3

Что начинающий Erlang/OTP должен знать о поведении?

Возможно, что написано здесь.

Можно ли описать и понять понятие поведения OTP в двух словах?

Чтение из документа: "Поведение - это формализация этих общих шаблонов. Идея состоит в том, чтобы разделить код для процесса в общей части (модуле поведения) и определенной части (модуль обратного вызова)".

Какая функция обратного вызова действительно означает в контексте Elang/OTP?

Посмотрите на ссылку выше, где приведены примеры функций обратного вызова.

Можно ли рассматривать обратные вызовы в реализации поведения, поскольку методы переопределяются в Java?

В терминах Java поведение, вероятно, будет интерфейсом Java, а обратным вызовом будет реализация одного из методов, определенных в интерфейсе.

В книге говорится, что связанная функция обратного вызова для библиотечной функции gen_server: start_link/4 в следующем коде: "Module: init/1". Означает ли это, что с init/1 мы вызываем библиотечную функцию gen_server: start_link/4? Или это означает что-нибудь еще?

Это означает, что каждый раз, когда вы вызываете gen_server: start_link/4, вызывается функция Module: init/1, где Module является вторым параметром, который вы передали функции start_link, с аргументами, которые вы предоставили в качестве четвертого аргумент. Другими словами, это то, что происходит за кулисами start_link/4:

...
start_link(Name, Module, Args, Opts) ->
  ...
  Module:init(Args)
  ...
...

Ответ 4

посмотрите исходный код модуля gen_server в каталоге erlang lib. Это очень хорошо объяснено в исходном коде, комментарии очень сложны.

Ответ 5

gen_server: start_link вызывает init.