Enum.map returns a list, not the enumerable type supplied

Hi,

I was wondering why Enum.map returns a list and not the enumerable type provided. eg:

iex> Enum.map(%{a: 1, b: 2, c: 3}, fn x -> x end)
[a: 1, b: 2, c: 3]

Wheres I would expect:

iex> Enum.map(%{a: 1, b: 2, c: 3}, fn x -> x end)
%{a: 1, b: 2, c: 3}

For me this seems un intuitive. The only thing I can think of is that this might not be a problem in elixir due to a preference for lists over other enumerable types?

I’m very new to elixir and BEAM architecture in general so sorry if this is a naive or redundant question.

Enum.map/2 always returns a list. If you want a map instead, use Enum.into/3:

iex> Enum.into(%{a: 1, b: 2, c: 3}, %{}, fn x -> x end)
%{a: 1, b: 2, c: 3}
1 Like

Map.new/2 returns what you expect:

iex(1)> Map.new(%{a: 1, b: 2, c: 3}, fn x -> x end) 
%{a: 1, b: 2, c: 3}
3 Likes

Thanks for your response.

Using a map was just an example. I’m more interested in the case with a general data structure that implements the Enumerable protocol. I guess what I’m interested in is why the Enum module was designed like this, it is different to how other programming languages I’ve seen behave.

It is specific to Elixir, Erlang uses functions that returns the same type.

But Elixir brings polymorphism with protocol, and that may be the reason why…

1 Like

What should Enum.map(%{a: 1, b: 2, c: 3}, fn {_, x} -> x end) return if it would always return the same structure as the input one?

3 Likes

Thats a good point.

I guess I probably change Enum.map to preserve the keys and just act on the values. Other approaches could be to just return a list or throw an error.

I’m not entirely sure how a Map works in elixir under the hood so there may be a much better solution or maybe something in the wider design means this has to be the way things are done. I’m just interested why really. Again it seems to me not to be the standard in other languages like Erlang as @kokolegorille pointed out.

1 Like

The reason why Enum always returns a list is because there are many Enumerable.t types, which invalidate their own constraints when mapped, like ranges or various streamed enumerables. E.g. what would Enum.map(1..10, & &1 * 10) return.

As alternative in elixir you have the Collectable protocol as well as all the erlang/datatype specific functions like the already mentioned Map.new.

7 Likes

Thanks, the Collectable is exactly what I was missing!