The Enumerable protocol today contains 3 functions: reduce/3, member?/2 and count/1. As the community grew, they asked for more extension points, such as fetch/2, but we didn鈥檛 add those functions as we did not see cases for them in the standard library itself.
With the addition of Date.Range, we have enough cases that require the specialization of the Enumerable protocol in the standard library itself to finally justify adding new functions to the protocol.
They are two: aggregate/2 and slice/1. Those are detailed below.
aggregate/2 is used to take general information from the collection, such as :count, :sum, :max and :min:
@spec aggregate(Enumerable.t, :sum | :min | :max | :count) :: {:ok, term()} | {:error, __MODULE__}
It should be implemented by data structures that can provide such data without traversing the whole collection. For example, ranges:
def aggregate(min..max, :sum) do
div((max - min + 1) * (min + max), 2)
end
def aggregate(min.._, :min) do
min
end
def aggregate(_..max, :max) do
max
end
def aggregate(min..max, :count) do
max - min + 1
end
def aggregate(_range, _) do
{:error, __MODULE__}
end
count/1 will no longer be used by Enum.
The idea of hiding all aggregations behind a single function is that most data types can continue to return {:error, __MODULE__} without implementing 4 or 5 different functions.
slice/1 will return how a data structure can be sliced contiguously. It will be used as the backbone for many functions in Enum, such as Enum.slice/3 itself, Enum.fetch/2, Enum.random/1 and others.
@spec slice(Enumerable.t) ::
{:ok, count :: non_neg_integer(), (start :: non_neg_integer, length :: pos_integer -> [term()])}
| {:ok, count :: non_neg_integer(), __MODULE__}
| {:error, __MODULE__}
It should return {:ok, count, slicing_fun} for data structures that can be sliced without traversing the whole collection. The count value is used to compute the slicing boundaries. The slicing function receives the start position, which is a number >= 0, and the length of the slice, which is always >= 1.
If the data structure has a known size but cannot be sliced without traversing the collection, it can still return {:ok, count, __MODULE__} which is also used to optimize the slicing operation.
I am tackling this one!
Closing in favor of #6958.
Most helpful comment
I am tackling this one!