针对初学者的Erlang / OTP行为

正如我从“Erlang and 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, "rn$", "", [{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).
    

    问: Erlang / OTP初学者应该了解哪些行为? 简而言之,是否可以描述和理解OTP行为的概念?

    行为通常用于代码中,以便编译器可以根据其行为(例如application / supervisor / gen_server / gen_event / gen_fsm)生成更直观的错误消息。

    它使编译器能够提供特定于ex:gen_server行为的错误消息

    问:在Elang / OTP的背景下,“回调函数”实际上意味着什么?

    可以说回调函数取自GUI编程(至少类似)。 每当事件发生时, 点击鼠标有一个单独的函数来处理鼠标点击。

    因此,每当例如。 一个gen_server的导出函数被另一个模块调用,该函数可以有一个具有不同模式的回调函数(handle_call / handle_cast)。

    问:我们能否将行为实现中的回调看作Java中覆盖的方法?

    是啊......也许......不是:)

    问:本书说在下面的代码中库函数'gen_server:start_link / 4'的相关回调函数是'Module:init / 1'。

    gen_server:start_link自己调用init函数,就像w55所回答的那样....(对不起,有一个很大的名字)。


    希望我已经回答了你所有的疑问:)


    我没有试图像其他答案那样解决您的具体问题,而是尝试用简单的术语解释行为背后的基础知识,并让您在理解这些基础知识的基础上回答自己的问题。

    行为基本上是一个消息处理框架,其中“框架”是指可以由最终用户完成和定制的问题的部分解决方案的经典定义。 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的初始值。

    我们的行为循环示例接收表单M1M2和任何其他消息的消息,其中的细节无关紧要,并且对于每条消息,在Callbacks模块中调用不同的回调函数。 在这个例子中, handle_m1handle_m2回调函数分别处理消息M1M2 ,而回调handle_other处理所有其他类型的消息。 请注意, State将传递给每个回调函数。 每个函数都要返回一个元组,第一个元素告诉循环下一步做什么,第二个元素包含可能的循环新状态 - 或者是与State相同的值,或者是一个新的不同值 - 循环存储在变量中NState 。 在这个例子中,如果Next是原子stop ,则循环停止,但如果是其他任何事情,循环会递归地调用自身,将新状态NState传递给下一次迭代。 而且由于它是尾递归的,所以循环不会吹出堆栈。

    如果您深入了解诸如gen_servergen_fsm等标准OTP行为的来源,您会发现一个类似于此的循环,但由于处理系统消息,超时,跟踪,异常等原因,它们更加复杂。在单独的进程中启动它们的循环,因此它们还包含用于启动循环进程并将消息传递给它的代码。


    什么是Erlang / OTP初学者应该了解的行为?

    可能是这里写的。

    简而言之,是否可以描述和理解OTP行为的概念?

    从doc中读取:“行为是这些常见模式的形式化,其思想是将通用部分(行为模块)和特定部分(回调模块)中的进程代码分开。”

    在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)
      ...
    ...
    
    链接地址: http://www.djcxy.com/p/38195.html

    上一篇: Erlang/OTP behaviors for beginner

    下一篇: Trouble Understanding Erlang Gen