What is the point of Spawn(Node, Fun) on erlang, if Node has to have the same module loadable as a client node?

时间:2016-08-31 17:19:09

标签: erlang distributed

Why create illusion that you are sending a Fun to remote node to execute in a new process? If client node has to have same module loadable with the Fun defined as a server node anyway. Why not only spawn(Node, M, F, A) then, which makes it clear that you are sending a definition of a function call, not Fun itself.

2 个答案:

答案 0 :(得分:5)

Let's consider two possible cases

Functions referring to module functions

Fun = fun file:getcwd/0,
erlang:spawn(Node, Fun). 

In this case Fun indeed should be loadable at the remote side.

Anonymous functions

Fun = fun() -> io:format("My node is ~p~n", [node()]) end,
erlang:spawn(Node, Fun). 

They are callable also.

Summarizing

While there some limitations implied to a function, that can be spwaned remotely, this form is still suitable and can be dropped, because then second case would become impossible

Also

Some misconceptions might go from this article

Actually, if you run erlang:fun_info for anonymous function, you'll see, that it provides implementation in a form of AST

(b@lol4t0-home)21> rp(erlang:fun_info(fun() -> io:format("My node is ~p~n", [node()]) end)). 
[{pid,<0.96.0>},
 {module,erl_eval},
 {new_index,20},
 {new_uniq,<<99,62,121,82,122,95,246,237,63,72,118,40,4,
             25,16,50>>},
 {index,20},
 {uniq,52032458},
 {name,'-expr/5-fun-3-'},
 {arity,0},
 {env,[{[],
        {eval,#Fun<shell.21.31625193>},
        {value,#Fun<shell.5.31625193>},
        [{clause,1,[],[],
                 [{call,1,
                        {remote,1,{atom,1,io},{atom,1,format}},
                        [{string,1,"My node is ~p~n"},
                         {cons,1,{call,1,{atom,1,node},[]},{nil,1}}]}]}]}]},
 {type,local}]

答案 1 :(得分:1)

下面的示例显示远程节点可以执行从未在其上加载的代码,甚至是对调用者节点上定义的匿名函数的调用:

节点上的

Erlang/OTP 19 [erts-8.0] [64-bit] [smp:4:4] [async-threads:10]

Eshell V8.0  (abort with ^G)
(titi@XXXXXXXX)1> net_adm:ping(toto@XXXXXXXX).
pong
(titi@XXXXXXXX)2> F = fun F(X) -> receive {plus,N} -> F(X+N); get -> io:format("state is ~p~n",[X]), F(X); stop -> bye end end. 
#Fun<erl_eval.30.52032458>
(titi@XXXXXXXX)3> G = fun() -> register(server,self()), F(0) end.
#Fun<erl_eval.20.52032458>
(titi@XXXXXXXX)4> spawn(toto@XXXXXXXX,G).
<7039.67.0>
state is 0           
state is 5           
state is 10          
(titi@XXXXXXXX)5> 
节点上的

Erlang/OTP 19 [erts-8.0] [64-bit] [smp:4:4] [async-threads:10]

Eshell V8.0  (abort with ^G)
(toto@XXXXXXXX)1> server ! get. % will print state is 0
get
(toto@XXXXXXXX)2> server ! {plus,5}. % new state = 5
{plus,5}
(toto@XXXXXXXX)3> server ! get. % will print state is 5    
get
(toto@XXXXXXXX)4> server ! {plus,5}. % new state = 10
{plus,5}
(toto@XXXXXXXX)5> server ! get. % will print state is 10     
get
(toto@XXXXXXXX)6> server ! stop. % ends the server process    
stop
(toto@XXXXXXXX)7> server ! get. % message will fail
** exception error: bad argument
     in operator  !/2
        called as server ! get
(toto@XXXXXXXX)8> 

我还检查过,如果用{plus,N} -> F(X+N);替换代码{plus,N} -> F(maps:put(X,X+N,maps:new()));,而TObjectList<TMember> 正在调用未在shell启动时加载的模块,那么它也能正常工作。

修改

阅读评论,似乎在shell中进行测试不是正确的方法,但结论是:

  • 不仅仅依赖shell来进行验证,
  • 应该有一种方法可以在远程节点上执行一个只在调用者节点上知道的代码,因为shell可以这样做...