Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/maven/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Erlang 如何参数化gen_服务器模块?_Erlang - Fatal编程技术网

Erlang 如何参数化gen_服务器模块?

Erlang 如何参数化gen_服务器模块?,erlang,Erlang,编辑: 我不打算使用参数作为构造Erlang程序的通用方法——我仍在学习传统的设计原则。我也不想模仿OOP。这里我唯一的一点是使gen_服务器调用在服务器实例之间保持一致。对我来说,这更像是修复了一个破碎的抽象概念。我可以想象这样一个世界,语言或OTP使得使用任何gen_服务器实例的api都很方便,这就是我想要生活的世界 感谢Zed证明我的主要目标是可能的 有人能想出在gen_服务器上使用参数化模块的方法吗?在下面的示例中,假设test_child是一个带有一个参数的gen_服务器。当我尝试启

编辑:

我不打算使用参数作为构造Erlang程序的通用方法——我仍在学习传统的设计原则。我也不想模仿OOP。这里我唯一的一点是使gen_服务器调用在服务器实例之间保持一致。对我来说,这更像是修复了一个破碎的抽象概念。我可以想象这样一个世界,语言或OTP使得使用任何gen_服务器实例的api都很方便,这就是我想要生活的世界

感谢Zed证明我的主要目标是可能的


有人能想出在gen_服务器上使用参数化模块的方法吗?在下面的示例中,假设test_child是一个带有一个参数的gen_服务器。当我尝试启动它时,我得到的只是:

42> {test_child, "hello"}:start_link().
** exception exit: undef
     in function  test_child:init/1
        called as test_child:init([])
     in call from gen_server:init_it/6
     in call from proc_lib:init_p_do_apply/3
最后,我试图找到一种使用gen_服务器的多个命名实例的方法。据我所知,一旦你开始这样做,你就不能再使用你的漂亮API了,你必须用gen_server:call和gen_server:cast向你的实例抛出消息。如果我能告诉实例它们的名字,这个问题就可以得到缓解

-module(zed, [Name]).
-behavior(gen_server).

-export([start_link/0, init/1, handle_cast/2]).
-export([increment/0]).

increment() ->
    gen_server:cast(Name, increment).

start_link() ->
    gen_server:start_link({local, Name}, {?MODULE, Name}, [], []).

init([]) ->
    {ok, 0}.

handle_cast(increment, Counter) ->
    NewCounter = Counter + 1,
    io:format("~p~n", [NewCounter]),
    {noreply, NewCounter}.
本模块对我来说运行良好:

Eshell V5.7.2  (abort with ^G)
1> S1 = zed:new(s1).
{zed,s1}
2> S1:start_link().
{ok,<0.36.0>}
3> S1:increment().
1
ok
4> S1:increment().
2
ok
Eshell V5.7.2(使用^G中止)
1> S1=zed:新的(S1)。
{zed,s1}
2> S1:启动链接()。
{好的,}
3> S1:增量()。
1.
好啊
4> S1:增量()。
2.
好啊

我认为您不应该以这种方式使用此功能。看起来您正在寻找与gen_服务器类似OO的接口。您正在为此使用本地注册的名称-这会在程序中添加大量的共享状态,这是一件坏事。只有关键服务器和中央服务器应该注册到
register
BIF-让所有其他服务器都不命名,并由位于它们之上的某种管理者管理(可能应该以某种名称注册)。

这个答案有两个部分。首先,您可能不想使用参数化模块,除非您非常精通Erlang。他们给你的只是一种不同的方式来传递论点

-module(test_module, [Param1]).

some_method() -> Param1.
相当于

-module(test_non_paramatized_module).

some_method(Param1) -> Param1.
前者根本买不到什么,现有的Erlang代码很少使用这种风格

更常见的做法是将name参数(假设您正在创建许多以不同名称注册的类似gen_服务器)传递给start_link函数

start_link(Name) -> gen_server:start_link({local, Name}, ?MODULE, [Name], []).

答案的第二部分是gen_server与参数化模块兼容:

-module(some_module, [Param1, Param2]).

start_link() -> 
  PModule = ?MODULE:new(Param1, Param2),
  gen_server:start_link(PModule, [], []).
Param1
Param2
将在所有gen_服务器回调函数中可用

正如Zed提到的,由于
start\u link
属于参数化模块,您需要执行以下操作才能调用它:

Instance = some_module:new(Param1, Param2),
Instance:start_link().
我发现这是一种特别难看的风格-调用
某些模块:new/n
的代码必须知道模块参数的数量和顺序。调用
some\u module:new/n
的代码也不能存在于
some\u module
本身中。如果模块参数的数量或顺序发生变化,这反过来会使热升级更加困难。您必须协调加载两个模块,而不是一个模块(
some_模块
及其接口/构造函数模块),即使您可以找到升级运行
some_模块
代码的方法。在一个次要的方面,这种风格使得为
某些模块:start\u link
使用的代码库增加一些难度


建议通过
gen_server:start_link/3,4
函数参数显式地将参数传递给
gen_服务器
,并将其存储在从
?模块:init/1
回调返回的状态值中

-module(good_style).

-record(state, {param1, param2}).

start_link(Param1, Param2) ->
  gen_server:start_link(?MODULE, [Param1, Param2], []).

init([Param1, Param2]) ->
  {ok, #state{param1=Param1,param2=Param2}}.
使用这种风格意味着您不会被OTP中尚未完全支持参数化模块(一种新的、仍然是实验性的特性)的各个部分所吸引。此外,可以在gen_服务器实例运行时更改状态值,但无法更改模块参数


此样式还支持通过代码更改机制进行热升级。调用
code\u change/3
函数时,可以返回新的状态值。没有相应的方法将新的参数化模块实例返回到
genu服务器
code.

我只想说两件事:

  • 阿切卢斯正确地解释了这一点。正如他所说的,他展示的最后一种方式是推荐的方式,并且做你期望的事情

  • 绝不,绝不,绝不,绝不使用您尝试的表单!这是从过去遗留下来的东西,它从来就不是你想要的,现在被强烈反对


您想让多台gen_服务器使用相同的回调模块,每个都有不同的名称?或者多个具有相同名称的gen_服务器?多个gen_服务器使用相同的回调模块。您可以向start_link传递一个名称以注册不同的“实例”。当您只有一个实例时,通常会将其命名为与模块相同的名称,然后您的公共API的工作方式是:some_module:some_function()。。。但这似乎只是一种方便。如果使用其他名称注册gen_服务器,它将不再工作。我想以如下内容结束:1>Mod:some_function()。2> Mod1:一些函数()。。。如果每个变量都指向gen_服务器模块的不同实例,或者,我想知道为什么我不应该关心这个问题。我看到的每一个gen_服务器简介都设置了一个API,而不仅仅是使用gen_服务器强制转换/调用。作为Erlang的新手,我希望能够轻松克隆许多进程,我很惊讶API功能在您重命名gen_服务器后立即中断。查看您的异常情况,您忘记将参数化形式的模块传递给gen_服务器:start_链接。请看我的答案,我不明白你第二部分的意思。参数化模块中没有“静态”函数(我也想念它们)。您将无法调用start\u link()w