What is the difference between spawn/1 and spawn/3?

I did the ping-pong exercise from the OTP guide book and I don’t understand the function of spawn/1 function.

Here is the code

lib/pingpong.ex

defmodule PingPong do
  def start do
   pong_pid = spawn(Pong.loop)
   ping_pid = spawn(Ping, :ping, [pong_pid])
  end
end

lib/pong.ex

defmodule Pong do
 def loop do
   IO.puts  "running"
   receive do
    {sender_pid , :ping} ->
    :timer.sleep(1_000)
    IO.puts "pong"
    send(sender_pid, {self, :pong})
   end
   IO.puts "ran"
   loop
   end
end

Now, when I did iex> PingPong.start it just printed “running” and just stood there. If I change the line 3 in PingPong to

# spawn/3
pong_pid = spawn(Pong, :loop, [])

it works. So spawn/1 just spawns the function. But what value does it have when it blocks the current process until the function has finished?

You’re misunderstanding a bit about what’s going on. spawn/1 is supposed to take an anonymous function IE:

spawn(fn -> Pong.loop end)

or just spawn(&Pong.loop/0)

You’re calling the function Pong.loop and then passing its result to spawn. However, Pong.loop just goes in an infinite loop, so your program sits waiting forever.

1 Like

Thanks. I tried as you wrote and indeed it works. But this brings another question.

Every function should return something. The way I understand it the anonymous function calls Pong.loop/0. Since Pong.loop/0 is the last declaration in the anonymous function then it’s return value is essentially the anonymous function’s return value. Why isn’t it the same as calling directly the Pong.loop/0?

I’m not sure what you’re saying. It sounds like you’re saying fn -> x() end is just equivalent to x. The difference is that if I do

y = x()

Then x() is called immediately, and y has the value of whatever x returns. If x is an infinite loop, the program never terminates.

However if I have

z = fn -> x() end

x() has not yet been called. It will only be called when the function that wraps it has been called, IE: z.(). z’s value is a function itself, not the result of the function. This means that if you pass z to spawn, spawn can start a new process and THEN call z.() in that new process. Calling z.() will evaluate the contents of the anonymous function from earlier, and then that’s when x() is called.

3 Likes

Thanks. It makes perfect sense now.