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]
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.
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.