Some people prefer Elixir syntax over Erlang syntax, that's their opinion, but if you are like me, you probably knows how Erlang explicitness and conciseness make everything better. I recently needed to quickly debug a piece of code on a running node using Elixir, and while doing that, spawning processes can be helpful to store information, simulate stuff and so on. In Erlang, a recursive anonymous function looks like that:
1> F = fun F() ->
receive
exit -> ok;
Msg -> io:format("~p~n", [Msg]),
F()
end
end.
#Fun<erl_eval.20.113135111>
2> P = spawn(F).
<0.151.0>
3> P ! {self(), test}.
{<0.113.0>,test}
{<0.113.0>,test}
The same is a bit different in Elixir, due to the syntax. Based on the documentation, it is not fixed, but if I'm wrong, don't hesitate to comment. Anyway, here the solution:
iex> f = fn (f) ->
receive do
:exit -> :ok
msg ->
IO.inspect(msg)
f.(f)
end
end
#Function<42.113135111/1 in :erl_eval.expr/6>
iex> p = spawn(fn -> f.(f) end)
#PID<0.725.0>
iex> send(p, {self(), :test})
{#PID<0.724.0>, :test}
{#PID<0.724.0>, :test}
Just to be clear, the same idea can also be done in Erlang, but with the syntactic sugar around the anonymous function, it's way easier to create recursive loop.
1> F2 = fun (F2) ->
receive
exit -> ok;
Msg -> io:format("~p~n", [Msg]),
F2(F2)
end
end.
#Fun<erl_eval.42.113135111>
2> P2 = spawn(fun() -> F2(F2) end).
<0.157.0>
3> P2 ! {self(), test}.
{<0.113.0>,test}
{<0.113.0>,test}
This method can be a bit more flexible though, because the state of the recursive function, is a function, one can create an interface to update it dynamically during code execution.
iex> f2 = fn (f2) ->
receive do
:exit -> :ok
{:update, fup} when is_function(fup) ->
fup.(fup)
msg ->
IO.inspect(msg)
f2.(f2)
end
end
#Function<42.113135111/1 in :erl_eval.expr/6>
iex> f3 = fn (f3) ->
receive do
:exit -> :ok
{:update, fup} when is_function(fup) ->
fup.(fup)
msg ->
IO.puts("f3: #{inspect msg}")
f3.(f3)
end
end
#Function<42.113135111/1 in :erl_eval.expr/6>
iex> p = spawn(fn() -> f2.(f2) end)
#PID<0.733.0>
iex> send(p, {self(), :test})
{#PID<0.724.0>, :test}
{#PID<0.724.0>, :test}
iex> send(p, {:update, f3})
{:update, #Function<42.113135111/1 in :erl_eval.expr/6>}
iex> send(p, {self(), :test})
{#PID<0.724.0>, :test}
f3: {#PID<0.724.0>, :test}
As you can see, the main loop can be updated on demand, useful when we need to debug or trace something quick, and insert receive patterns.
Top comments (0)