Craftsman at Work

I'm Artur Karbone, coding software architect and independent IT consultant and this is my blog about craftsmanship, architecture, distributed systems, management and much more.

Elixir Enum Module Under the Hood #3

In this series of posts we are gooing to look under the hood of Enum module. Here are the source and tests of the module.

Today our method under research is ubiquitous map. Let's go ahead, open iex and type h Enum.map .

iex> h Enum.map  

Help system shows that the method has two signatures. The first one works with lists and the second one with dictionaries :

┃ iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
┃ [2, 4, 6]
┃ 
┃ iex> Enum.map([a: 1, b: 2], fn({k, v}) -> {k, -v} end)
┃ [a: -1, b: -2]

Here is the source:

@doc """
  Returns a new collection, where each item is the result
  of invoking `fun` on each corresponding item of `collection`.
  For dicts, the function expects a key-value tuple.
  ## Examples
      iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
      [2, 4, 6]
      iex> Enum.map([a: 1, b: 2], fn({k, v}) -> {k, -v} end)
      [a: -1, b: -2]
  """
  @spec map(t, (element -> any)) :: list
  def map(collection, fun) when is_list(collection) do
    for item <- collection, do: fun.(item)
  end

  def map(collection, fun) do
    reduce(collection, [], R.map(fun)) |> :lists.reverse
  end

As we see the method has a guard when is_list, which means that it is not possible to pass something other than list. Otherwise pattern matching is not going to be happy.

Under the hood the map method uses comprehensions:

 for item <- collection, do: fun.(item)

Comprehensions are syntactic sugar for manipulating lists.A comprehension is made of three parts: generators, filters and collectables. In our particular case item<-collection is a generator. fun.(item) is an anonymous function applied to any item within collection.

Before I start reading code I usually think of how my own naive implementation of the feature would look like. This time I've came to the following conclusion:

defmodule MyEnum do  
    def map([h|t],fun), do: [fun.(h)| map(t,fun)]
    def map([],_), do: []
end  
comments powered by Disqus