greggreg
greggreg

Reputation: 12085

Tail recursion in a receive block

I see functions that enclose a receive block getting called recursively seemingly not in the tail position all over the place in elixir example code. For example:

defmodule A do
  def loop do
    receive do
      :ping ->
         IO.puts "Received :ping"
         loop # <- Tail position?
      :pong ->
         IO.puts "Received :pong"
         loop # <- Also Tail position?
      after
        5000 ->
          loop # <- Also Tail position?
     end
     loop # <- Also Tail position?
  end
end

Is receive a special construct that optimizes for tail positions at the end of all match blocks? If so does it apply if the receive block has an after block? What if there is code after the receive block in the function?

Upvotes: 1

Views: 451

Answers (1)

Patrick Oscity
Patrick Oscity

Reputation: 54684

In your example, only the very last call to loop is tail recursive. Instead of making the recursive call inside the receive block, just let the receive return and just use the tail-recursive call to loop at the end of the function that is already there.

defmodule A do
  def loop do
    receive do
      :ping ->
         IO.puts "Received :ping"
      :pong ->
         IO.puts "Received :pong"
      after
        5000 ->
          nil
     end
     loop
  end
end

As a general rule, process loops should always have exactly one recursive call at the end of the loop function. That's the simplest way to guarantee that the process will be able to loop infinitely.

However, you can loop inside the receive block if it is the last statement in the loop function. The result will also be tail-recursive. This allows you to conditionally stop the loop based on a received message

defmodule A do
  def loop do
    receive do
      :ping ->
         IO.puts "Received :ping"
         loop
      :pong ->
         IO.puts "Received :pong"
         loop
      :stop ->
         IO.puts "Bye Bye"
     end
  end
end

Also see http://erlang.org/doc/efficiency_guide/processes.html#id69762

Upvotes: 5

Related Questions