Reputation: 905
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.
Upvotes: 6
Views: 887
Reputation: 14042
The example below shows that the remote node can execute a code that was never loaded on it, even a call to an anonymous function that was defined on the caller node:
on node titi
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>
on node toto
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>
I also checked that if you replace the code {plus,N} -> F(X+N);
by {plus,N} -> F(maps:put(X,X+N,maps:new()));
which is calling a module not loaded by the shell at start, it works fine as well.
Edit
Read the comments, it seems that doing the test in the shell is not the right way, 2 conclusions though:
Upvotes: 2
Reputation: 12557
Let's consider two possible cases
Fun = fun file:getcwd/0,
erlang:spawn(Node, Fun).
In this case Fun
indeed should be loadable at the remote side.
Fun = fun() -> io:format("My node is ~p~n", [node()]) end,
erlang:spawn(Node, Fun).
They are callable also.
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
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}]
Upvotes: 6