Creating infinite sequences with Stream.unfold/2

Creating infinite sequences daily?

Me neither.

But it’s really cool to see how we can do that with Stream.unfold/2. And save that knowledge in some part of your brain for the day you need it.

You can think of unfold as something like the opposite of reduce.

When we reduce, we grab a list (or sequence) and iterate over the values to generate a final value. With unfold, we start with a single value (the seed) and then generate a sequence by applying a transformation on each subsequent value.

Finite sequence

We can use Stream.unfold/2 to generate a finite sequence. If we return nil the generation will stop.

stream = Stream.unfold(0, fn
  10 -> nil
  i -> {i, i + 1}
end)

stream |> Enum.to_list()
#=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Infinite sequence

To generate an infinite sequence, we can modify the previous sequence to remove the 10 -> nil pattern match.

infinite_stream = Stream.unfold(0, fn
  i -> {i, i + 1}
end)

infinite_stream |> Enum.take(10)
#=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Note how we use Enum.take/2 instead of to_list/1 so we can create the first 10 elements of the sequence.

Fibonacci sequence

fibonacci = Stream.unfold(0, fn
  0 -> {0, 1}
  1 -> {1, {1, 1}}
  {1, 1} -> {1, {1, 2}}
  {ix, iy} -> {iy, {iy, ix + iy}}
end)

fibonacci |> Enum.take(12)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

An even better version of the fibonacci sequence (not shown on the video) would be like this:

fibonacci = Stream.unfold(0, fn
  0 -> {0, {0, 1}}
  {ix, iy} -> {iy, {iy, ix + iy}}
end)

fibonacci |> Enum.take(12)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Want the latest Elixir Streams in your inbox?

    No spam. Unsubscribe any time.