Recursion or Stream to map a list in Elixir

I’m exploring Elixir and I would like to know the best practice to manipulate tuples, lists… enumerables.

I implemented my own map function, then I realized that the Stream module provides the same functions I was going to create…

defmodule ListUtil do
  def map([head | tail], func) do
    [func.(head) | maps(tail, func)]
  end

  defp map([], func) do
    []
  end
end

mapped = ListUtil.map([1,2,3], &(&1 * 2)) # gives [2,4,6]

With streams

mapped = Enum.to_list(Stream.map([1,2,3], &(&1 * 2))) #gives [2,3,4]

What difference between my code and the stream one? Performance/Utility meaning
Am I supposed to use streams to manipulate enumerables in Elixir?

The main difference between your coude and the Stream.map/2 is basically, that the Streamed version is lazyly evaluated, while yours is eager.

Also the Streamed version does work with any datatype that implements the Enumerable protocol.

Also there is another version of map/2 in the standard library called Enum.map/2 which implements an eager map for any datatype that implements Enumerable.

Your function is specialised to a list and available as :lists.map/2 (erlang docs) with swapped arguments.

The idiomatic way is to use Enum or Stream, depending on the fact if you need eager or lazy evaluation.

1 Like

I see, thanks a lot

Another thing, I don’t see the real difference between Eager and Lazy map. I know what it means but I can’t imagine this on a map. At what point is this eager or lazy?

Steams are just composing steps of “what to do” until you decide to retrieve items of the steam a.k.a. it’s no longer possible to hold back on really executing those steps.

Enum.map(1..100_000, fn x -> x + 1 end) |> Enum.take(2)
Stream.map(1..100_000, fn x -> x + 1 end) |> Enum.take(2)

The first one does add 1 to each number in the list before taking the first two. The second one will only add 1 to the first two items and be done.

2 Likes