Elixir Streams
A stream is a lazy plan for producing items one at a time when something asks for them. Building a stream does no work until an Enum function consumes the plan.
A stream runs when Enum consumes it
Stream.map only plans to add 500 to each number. Enum.to_list runs the plan and collects the three results.
plan = Stream.map([1000, 2000, 3000], fn n -> n + 500 end)
IO.inspect(Enum.to_list(plan))Stream steps chain like Enum steps
The filter plan keeps numbers of 100 or more, and the map plan doubles them. One Enum.to_list call runs the whole chain.
result =
[120, 45, 300, 80, 210]
|> Stream.filter(fn n -> n >= 100 end)
|> Stream.map(fn n -> n * 2 end)
|> Enum.to_list()
IO.inspect(result)Take reads only the start of a huge range
Both streams plan over a million numbers, yet each take returns at once. Only the items asked for are ever produced.
first_three =
1..1_000_000
|> Stream.map(fn n -> n * 2 end)
|> Enum.take(3)
IO.inspect(first_three)
first_matches =
1..1_000_000
|> Stream.filter(fn n -> rem(n, 1000) == 0 end)
|> Enum.take(2)
IO.inspect(first_matches)Example
report =
1..1_000_000
|> Stream.map(fn n -> n * 3 end)
|> Stream.filter(fn n -> rem(n, 2) == 0 end)
|> Enum.take(4)
IO.inspect(report)
IO.inspect(Stream.map(["a", "b"], fn w -> String.upcase(w) end) |> Enum.to_list())Key points
- Stream module: Stream functions describe transformations without running them.
- Laziness: a stream does its work only when an Enum function asks for results.
- Building: Stream.map and Stream.filter return streams, not lists.
- Consuming: Enum.to_list runs a stream and collects every result into a list.
- Taking a few: Enum.take runs a stream only far enough to produce the items asked for.
In this example, the stream triples each number and the filter keeps only the even results. Enum.take stops the work after four matches, giving [6, 12, 18, 24] from a million-number range. A second stream then uppercases two short words when Enum.to_list consumes it.
Note
a stream over a million numbers can still answer a three-item request right away, because it never builds the full list.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does 1..100 |> Stream.map(fn n -> n + 1 end) |> Enum.take(2) return?