Ruby vs. Elixir: #Each

Ruby vs. Elixir: #Each
Down Arrow
Ruby

Rubyists use #each all over the place to iterate though collections. It's super common to see code along these lines:

[1, 2, 3].each {|num| puts num}

What #each does is execute the given block on every element of the given collection and then return the collection. It's usually used for side effects, rather than the return value. Array implements this method, as do Hash, Range, and, even more interestingly, CSV.

So how do I do the same thing in Elixir?

This seemingly simple question gets at some of the difference between these two languages, and so I’ll explore it in a little depth. Here's a look at Ruby vs. Elixir: #Each.

Elixir

Here is some Elixir code roughly equivalent to the above:

Enum.each([1, 2, 3], fn num -> IO.puts num end)

# Or with function shorthand syntax

Enum.each([1, 2, 3], &(IO.puts &1))

# Or most compact

Enum.each([1, 2, 3], &IO.puts/1)

# Or perhaps most *Elixirishly*

[1, 2, 3] |> Enum.each(&IO.puts/1)

Look ma, no methods!

Notice that we’re not invoking a method on the array itself (this is actually a list, rather than an array). Elixir has types (list is a type) and functions (#each is a function scoped to the Enum module). It does not have objects which implement methods. We’re feeding the list into Enum.each and also giving that function an anonymous function in the second parameter to invoke on every member of the list.

As for the versions above, The first is quite parallel.  The second uses the shorthand inline function syntax (kind of similar Ruby’s symbol to proc). The &1 is the first parameter passed to the function.

The third version is most compact, but the most foreign looking. This second parameter demonstrates another version of the shorthand syntax that wraps a function (for which you specify the arity with the /1) and then passes the first argument implicitly to that function.

The fourth is the most "Elixirish," as it uses the famous |> or pipe operator. This operator creates a pipeline, passing the data along the chain and calling the functions with the data (or return value of a previous function) as the first parameter of the function call.

Pipe allows you to build series of transformations like so:

iex> [1, 2, 3, 4] |> Enum.filter(&Integer.is_even/1) |> Enum.map(&(&1 * 3))
[6, 12]

It's worth noting as a sidebar that lists in Elixir are implemented as linked lists, so updating them is fast as long as you are prepending elements. Tuples, on the other hand, are more similiar to Ruby arrays, as their elements are stored contiguously in memory.  This makes accessing an element by index or finding their length quite fast, but updating a relatively slow operation as the whole tuple must be copied in memory. A good discussion of this can be found in the Elixir docs.

So there you have it, #each in Elixir. There is a lot more to the story, actually, like how it’s possible to enumerate through different kinds of collections that aren’t lists (yes, you can pass in tuples, maps, etc into Enum.each). I’ll get into how this works in future posts, so stay tuned!

 

Zander Mackie

Zander Mackie

Stride Alum

No items found.
green diamond