Hashrocket.com / blog

Large elixir cauldron

Elixir Streams for Lazy Evaluation

posted on and written by Johnny Winn in

Image 100x100 johnny winn

Lazy evaluation is a great way to delay the execution of larger datasets. In a typical enumeration each item is evaluated one-by-one. This isn't a problem with smaller sets but as those sets get larger the amount time to process grows exponentially. Every function has to evaluate the entire set before the next function can execute. Elixir's Stream module allows us to compose our enumerations prior to execution. Let's look at some examples.

First will take a look at using the Enum module to filter a range of numbers and take from the list.


  iex(1)> Enum.filter(1..10000000000000000, &(rem(&1, 3) == 0 || rem(&1, 5) == 0)) |> Enum.take 5

Wait for it... No really because it's going to take awhile. See, the Enum.filter function has to complete its iteration over the entire set before it can pipe the filtered list to the take function. However, if we use the Stream module we can compose the computation prior to executing.


  iex(1)> Stream.filter(1..10000000000000000, &(rem(&1, 3) == 0 || rem(&1, 5) == 0)) |> Enum.take 5
  [3, 5, 6, 9, 10]

Chaining Streams

When you compose a chain of streams it's easy to see how the execution differs from the typical chain of Enum functions. We can borrow the next example from the Stream module docs.

First we look at an example with piping the Enum functions together. You will note that each map is applied in order so first all the numbers are printed then they are doubled and printed.

1..3 |>
  Enum.map(&IO.inspect(&1)) |>
  Enum.map(&(&1 * 2)) |>
  Enum.map(&IO.inspect(&1))
1
2
3
2
4
6
#=> [2,4,6]

Now we can look at the same example composed with Streams.

stream = 1..3 |>
  Stream.map(&IO.inspect(&1)) |>
  Stream.map(&(&1 * 2)) |>
  Stream.map(&IO.inspect(&1))
Enum.to_list(stream)
1
2
2
4
3
6
#=> [2,4,6]

Now we see that the each number is completely evaluated before moving to the next number in the enumeration.

That's just a quick look at the power of streams in Elixir for lazy evaluation.

Posted in Elixir and tagged with Elixir