Jim

Jim

Endless recursion

I’m new to Elixir, and from what I read heavy use of recursion is mandatory. With that in mind, I look at this simple blink example I found:

defmodule FlightController do
  use Application

  @blink_duration 150 # ms
  @led_pin 18
  @gpio_on 1
  @gpio_off 0

  def start(_type, _args) do
    {:ok, pid} = Gpio.start_link(@led_pin, :output)

    spawn fn -> blink_forever(pid) end

    {:ok, self}
  end

  def blink_forever(pid) do
    Gpio.write(pid, @gpio_on)
    :timer.sleep @blink_duration
    Gpio.write(pid, @gpio_off)
    :timer.sleep @blink_duration

    blink_forever(pid)
  end
end

In my non-Elixir experience, recursive programs build a new stack with each call and release it when the last recursive call returns. You can’t do it forever because you’ll be out of stack space. So, this example couldn’t work on another platform. I have not run it but I’m guessing it does work.

My question, then, is, how does Elixir / BEAM implement recursion?

Most Liked

rvirding

rvirding

Creator of Erlang

Actually the tail call optimisation is done for all last calls not just recursive calls. It is sometimes called last-call optimisation. So you can write:

defmodule Foo do
  def bar() do
    baz()
  end

  def baz() do
    bar()
  end
end

which will happily recurse forever without building stack. It also works with inter-module calls.

This is the classic way of implementing state machines: you change state by calling the next state function as a tail call. You could view the Foo module as implementing a state machine with 2 states bar and baz.

peerreynders

peerreynders

Joe Armstrong: [erlang-questions] Tail call optimization

The correct name for the optimization used in Erlang is “last call optimization”.

cmkarlsson

cmkarlsson

The BEAM uses Tail Call Optimization (TCO) to avoid blowing the stack.

As an example:

def fac(1), do: 1
def fac(n), do: n * fac(n -1)

is not TCO because the recursive can only be done when it is the last operation of the function. In the fac function above it needs to keep the frame to do the multiplication.

This can be re-written in a TCO fashion by using an accumulator to hold the result.

def fac(n), do: fac(n, 1)

def fac(1, result), do: result
def fac(n, result), do: fac(n - 1, n * result)

In your case the blink_forever(pid) call is the last in the function and hence will use TCO.

Where Next?

Popular in Questions Top

RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Other popular topics Top

nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

We're in Beta

About us Mission Statement