It’s almost Christmas, which means the 2017 edition of Advent of Code is officially upon us! While studying other people’s solutions to the first few problems, Sasa Juric‏’s use of streams stood out to me.

The `resource/3` and the closely related `iterate/2` and `unfold/2` functions in the `Stream` module were completely unknown to me and seemed worthy of some deeper study.

After spending time researching and banging out a few examples, I realized that these functions are extremely useful and underrated tools for generating complex enumerable sequences in Elixir.

Let’s dive into a few examples to find out why!

## Simple Sequences

The `resource/3`, `iterate/2`, and `unfold/2` functions in the `Stream` module are all designed to emit some new values based on previous information. While they all accomplish the same task, they each have their own nuances.

`Stream.iterate/2` is best suited for generating simple sequences. We’ll define a “simple sequence” as a sequence who’s next value can entirely be determined by its preview value.

For example, we can implement a simple incrementing sequence:

``````
Stream.iterate(0, &(&1 + 1))
``````

Taking the first `5` values from this stream (`|> Enum.take(5)`) gives us `[0, 1, 2, 3, 4]`.

We ramp the complexity up a notch and generate a Mandelbrot sequence for a given value of `c`:

``````
defmodule Mandelbrot do
def sequence({cr, ci}) do
Stream.iterate({0, 0}, fn {r, i} ->
# z₂ = z₁² + c
{(r * r - i * i) + cr, (i * r + r * i) + ci}
end)
end
end
``````

The value of `z₂` is entirely determined by the value of `z₁` and some constant.

## Sequences with Accumulators

There are times when the next value in a sequence can’t be generated with the previous value alone. Sometimes we need to use other information we’ve built up along the way to generate the next value in our sequence.

A perfect example of this type of sequence is the Fibonacci sequence. To generate the `n`th value in the Fibonacci sequence, we need to know the previous value, `n-1`, and also the value before that, `n-2`.

This is where `Stream.unfold/2` shines. The `unfold/2` function lets us build up an accumulator as we generate each value in our sequence.

Here’s an stream that generates the Fibonacci sequence:

``````
Stream.unfold({0, 1}, fn {a, b} -> {a, {b, a + b}} end)
``````

`Stream.unfold/2` takes the initial value of our accumulator and a `next_fun` function. Our `next_fun` function returns a tuple who’s first element is the value emitted by our stream, and who’s second element is the accumulator that will be passed into the next call to `next_fun`.

Taking the first `10` elements of this stream gives us a familiar sequence of numbers:

``````
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
``````

For a more complex example of the power of `Stream.unfold/2`, check out how I implemented a stream that generates an integer spiral as part of an Advent of Code challenge.

## Multiple Values per Accumulator

Sometimes we’re faced with a true doozy of a sequence, and even an accumulator isn’t enough to accurately describe the next value. For example, what if we need to emit multiple sequential values for each invocation of our `next_fun`?

`Stream.resource/3` to the rescue!

The `Stream.resource/3` function was originally intended to consume external resources, hence its name and `start_fun` and `after_fun` functions, but it also works beautifully for building sufficiently complex sequences.

Wolfram-style cellular automata, such as Rule 30, is a perfect example of this type of complex sequence.

To generate our Rule 30 sequence, we’ll start with an initial list of values (usually just `[1]`). Every time we call our `next_fun`, we want to calculate the entire next layer in one go (which would be `[1, 1, 1]`). Unfortunately, the size of these layers continues to grow (the next would be `[1, 1, 0, 0, 1]`, etc…).

How would we write a stream that outputs each cell of each layer in order, and tells us the cell’s value and depth?

``````
defmodule Rule30 do
def stream(values) do
Stream.resource(fn -> {values, 0} end, &next_fun/1, fn _ -> :ok end)
end

defp next_fun({values, layer}) do
next = ([0, 0] ++ values ++ [0, 0])
|> Enum.map(&rule/1)
{values |> Enum.map(&{layer, &1}), {next, layer + 1}}
end

defp rule([1, 1, 1]), do: 0
defp rule([1, 1, 0]), do: 0
defp rule([1, 0, 1]), do: 0
defp rule([1, 0, 0]), do: 1
defp rule([0, 1, 1]), do: 1
defp rule([0, 1, 0]), do: 1
defp rule([0, 0, 1]), do: 1
defp rule([0, 0, 0]), do: 0
end
``````

`Stream.resource/3` makes short work of this potentially difficult problem. We start with our initial set of `values`, and a depth of `0`. Each call to our `next_fun` tacks on a few buffering `0`s to each side of our `values` list, and applies the `rule` function to each chunk of `3` values. Once we’ve finished calculating our `next` layer, we emit each value grouped with the current `depth` along with our updated accumulator tuple.

We can take the first `9` values from this stream and notice that they match up perfectly with what we would expect from Rule 30:

``````
Rule30.stream([1])
|> Enum.take(9)
|> IO.inspect
# [                {0, 1},
#          {1, 1}, {1, 1}, {1, 1},
#  {2, 1}, {2, 1}, {2, 0}, {2, 0}, {2, 1}]
``````

## Final Thoughts

While we’ve mostly talked about mathematical sequences here, these and techniques ideas apply to any type of enumerative data that needs to be generated.

In closing, here are a few rules to remember when working with `Stream.iterate/2`, `Stream.unfold/2`, and `Stream.resource/3`:

• Use `Stream.iterate/2` when you want to emit one value per call to your `next_fun` function, and that value can be entirely generated from the previously emitted value.
• Use `Stream.unfold/2` when you want to emit one value per call to your `next_fun` function, but need additional, accumulated data to generate that value.
• Use `Stream.resource/3` when you want to emit multiple values per call to your `next_fun` function.

I highly encourage you to check out Elixir streams if you haven’t. The `Stream` module is full of ridiculously useful tools to help streamline your data pipeline.