DEV Community

Lasse Skindstad Ebert
Lasse Skindstad Ebert

Posted on

Iterating in Elixir

This story was originally posted by me on Medium on 2016-10-07, but moved here since I'm closing my Medium account.

Like many Elixir developers I come from Ruby. Elixir is my first functional language after years of Java, VB.Net, C# and Ruby. And a little bit of Javascript.

One of the first things that stroke me as being different in functional programming, was how I should map, reduce, loop and otherwise iterate data models.

The each

In Ruby we might find something like this:

def concat_messages(items)
  result = ""
  items.each do |item|
    result += item.message if item.ok?
  end
  result
end

The issue of converting this to Elixir, is that everything is immutable in Elixir.

Trying to do the same thing would yield an empty result, since everything that happens inside Enum.each stays inside Enum.each. It’s basically the Las Vegas of iterations. You only bring home the actions with side effects, like writing to the console or getting married.

There are a number of ways to write the same code in Elixir. Let’s try with Enum.filter and Enum.reduce:

def concat_messages(items) do
  items
  |> Enum.filter(&Item.ok?/1)
  |> Enum.reduce("", &(&2 <> Item.message(&1))
end

This looks really nice! It exposes the intention of the code and makes it more readable.

Couldn’t we do the same thing in Ruby? Yes, easily. The only problem is that a lot of the loop-like code with mutable state exists in Ruby code bases. Writing the Elixir code above in Ruby would give us this equivalent:

def concat_messages(items)
  items
    .select(&:ok?)
    .reduce("") { |acc, item| acc + item.message }
end

Be a good Ruby programmer. Don’t mutate state when you don’t need to.

The loop

In many programming languages it is possible to loop and break out of the loop. Something like this:

def fetch_something
  tries = 0
  body = nil
  loop do
    status, body = make_external_http_call
    break if status == 200
    tries += 1
    if tries >= 5
      body = "Service not available" 
      break
    end
  end
  body
end

Trying to get an overview of this code takes a while :( It can break out of the loop two different places, and it mutates state between loop iterations.

How could be better? Let’s force ourselves to immutability with Elixir and rewrite the code:

def fetch_something(tries \\ 0)

def fetch_something(tries) when tries < 5 do
  case make_external_http_call do
    {200, body} -> body
    {_status, _body} -> fetch_something(tries + 1)
  end
end

def fetch_something(tries) when tries >= 5 do
  "Service not available"
end

This was a big improvement in the Ruby code too! Of course, the lack of pattern matching and guard clauses makes it not-as-nice as the Elixir version.

Conclusion

It might be hard at first to rewrite mutable, object-oriented iterations to an immutable functional language. For a long time it felt like I had to think backwards.

But for some reason, immutability forces you to write easy-to-read and concise code, which is clearly a long-term benefit of any software project.

I still write Ruby sometimes, and when I do, I tend to have a more functional code style than before I met Elixir. It helps me reason about my code and I avoid mixing logic and data. I have become a fan of the Ruby keyword module_function which allows you to define a module with pure functions in it, much like in Elixir.

Elixir and immutability is here to stay, and I see a big future for Elixir coming up.

Top comments (0)