为什么我们必须导出spawn使用的函数?

时间:2013-10-29 23:29:15

标签: concurrency erlang

在Erlang中,在处理进程时,必须导出spawn函数中使用的函数。

-module(echo).
-export([start/0, loop/0]).

start() ->
  spawn(echo, loop, []).

本书的原因"编程Erlang,第2版。第188页"是

  

"请注意,我们还必须从模块中导出spawn的参数。这是一个很好的做法,因为我们将能够在不更改客户端代码的情况下更改服务器的内部详细信息。"

在书" Erlang Programming",第121页:

-module(frequency).
-export([start/0, stop/0, allocate/0, deallocate/1]). 
-export([init/0]).  

%% These are the start functions used to create and 
%% initialize the server.

start() ->
   register(frequency, spawn(frequency, init, [])).

init() ->
   Frequencies = {get_frequencies(), []}, 
   loop(Frequencies).
  

请记住,在生成进程时,必须导出spawn / 3 BIF使用的init / 0函数。我们已将此函数放在单独的export子句中,以将其与客户端函数区分开来,客户端函数应该从其他模块调用。

请您向我解释一下这个理由背后的逻辑吗?

3 个答案:

答案 0 :(得分:2)

简短的回答是:spawn不是'语言构造'它的库函数。

这意味着'spawn'位于另一个模块中,该模块无法访问模块中的任何函数但导出。

你必须以某种方式传递给'spawn'函数来启动你的代码。它可以是函数值(即spawn(fun() -> (any code you want, including any local functions invocations) end))或模块/导出函数名称/参数,从其他模块可以看到。

答案 1 :(得分:1)

执行spawn时,您可以使用自己的环境和执行线程创建一个新的完全 进程。这意味着您不再在调用spawn的模块内“执行”,因此您必须对模块进行“外部”调用。模块中唯一可以从“外部”调用的函数是导出函数,因此必须导出生成的函数。

看到你在同一个模块中产生一个函数似乎有点奇怪,但这就是原因。

我认为重要的是要记住模块只是代码而不包含任何更深层的含义,例如像OO语言中的类。因此,即使您在不同进程中执行来自同一模块的函数, 非常 常见事件,它们之间也没有隐式连接。您仍然必须在进程之间发送消息,即使它是来自/到同一模块中的函数。

修改

关于将导出init/1放入单独的导出声明中的引用的问题的最后部分。没有必要这样做,它没有语义意义,您可以根据需要使用尽可能多的导出声明。因此,您可以将所有函数放在一个导出声明中,或者为每个函数分别设置一个函数;没有区别。

分割它们的原因纯粹是视觉和文档目的。您通常将函数分组为单独的导出声明,以便更容易看到它们是一个组。您还通常在单独的导出声明中放置“内部”导出函数,这些函数不是用户直接调用的函数。在这种情况下,必须为spawn导出init/1,但不能直接在spawn之外调用。

通过让用户调用start/0函数来启动服务器并且没有明确生成它们,init/1函数允许您根据需要更改内部。用户只能看到start/0功能。这是第一句引用的内容。

答案 2 :(得分:-1)

如果您想知道为什么必须导出任何内容并且默认情况下看不到所有内容,那是因为如果您隐藏了他们不应该隐藏的所有功能,那么用户应该调用哪些功能更清楚。这样,如果您改变实施的想法,使用您的代码的人将不会注意到。否则,可能有人正在使用您想要更改或消除的功能。

例如,假设您有一个模块:

-module(somemod).

useful() ->
    helper().
helper() ->
    i_am_helping.

您想将其更改为:

-module(somemod).

useful() ->
    betterhelper().
betterhelper() ->
    i_am_helping_more.

如果人们只应该致电useful,您应该可以进行此更改。但是,如果所有内容都已导出,那么人们可能会依赖helper。这种变化会破坏他们的代码。