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


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


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

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.


Thanks. It makes perfect sense now.