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.

Let's start with the count method. Let's go ahead and open iex and type h Enum.count .

iex> h Enum.count

Help system shows that the method has two signatures (with and without lambda) :

iex> Enum.count([1, 2, 3]) 3 iex> Enum.count([1, 2, 3, 4, 5], fn(x) -> rem(x, 2) == 0 end) 2

Here is the first one:

@doc """ Returns the collection's size. ## Examples iex> Enum.count([1, 2, 3]) 3 """ @spec count(t) :: non_neg_integer def count(collection) when is_list(collection) do :erlang.length(collection) 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 satisfied.

In addtion to that we see that internally the count method uses erlang.length method. Btw. looking at the code we have a glue how to document it via @doc directive.

There is another pattern matching for the same signature:

def count(collection) do case Enumerable.count(collection) do {:ok, value} when is_integer(value) -> value {:error, module} -> module.reduce(collection, {:cont, 0}, fn _, acc -> {:cont, acc + 1} end) |> elem(1) end end

This pattern matching basically works with collections. For instance:

iex> Enum.count 1..100

Internally Enumerable.count method is being called, which returns a tuple {:ok,value} (in this case value is being extracted from pattern matching and returned to the caller) or {:error,module}.

Let's dive into the second signature, wich allows to pass lambda and filter out the results.

@doc """ Returns the count of items in the collection for which `fun` returns a truthy value. ## Examples iex> Enum.count([1, 2, 3, 4, 5], fn(x) -> rem(x, 2) == 0 end) 2 """ @spec count(t, (element -> as_boolean(term))) :: non_neg_integer def count(collection, fun) do Enumerable.reduce(collection, {:cont, 0}, fn(entry, acc) -> {:cont, if(fun.(entry), do: acc + 1, else: acc)} end) |> elem(1) end

Again internally this method leverages reduce method. We are going to research the way reduce works internally later on. For now You can think of reduce like it is an accumulator wich receives a collection, accumulator value, current element of collection (while iterating through) and of course lambda which receives current element and accumulator as parameters.

Now let's see how tests are implemented. Basically there are two testing scenarios for each pattern matching and for each method signature. The tests are written against empty and respectivelly non empty lists/collections of different size: