This is a long post, and I think it might not be a popular one. Still, I
think it has an important punchline, so bear with me.

Proposal: Add Enum.fold/2 and Enum.fold/3 (and the Stream counterparts).
These both work like Enum.reduce, but flip the arguments in the reducer
function.

As I teach and code Elixir, I have four steps for most students. First, we
build common vocabulary and language constructs of Elixir. Next, we think
in terms of a common pattern I call constructor-reducer-converter. (More in
a sec.) Then, we apply that pattern in higher level constructs like
LiveView and OTP. Finally, we build projects and let students code these
patterns for themselves. In a phrase, *everything is reduce*. I want to
focus on that second step.

I am thinking and teaching more about one common pattern. At its core,
Elixir modules have functions around a common type T that either produce,
transform, or consume T. I call these functions constructors, reducers, and
converters. They all interact with type T.

Think of them in this way:

constructor(inputs) |> reducer(...) |> reducer(...) ... |> converter

This is the way the data-centric modules in Elixir are built today. In
Designing Elixir Systems with OTP speak, they are the *core* modules, and I
believe many Elixir developers build code this way, even if we don't
vocalize it in this way.

Building code like this puts us in conflict in one important way:

1. Elixir modules are generally organized around data of a common type
(let's say T)
2. Named functions in a module have T as the first argument.
3. Pipes rely on T as a first argument.
4. In Enum.reduce, T is the accumulator, and it's the second argument of
the reducer.

3 and 4 are in conflict! The accumulator plays the role of T in the
reducer:

reducer(any(), T) :: T

That's backwards, and it won't pipe.

If we have this:

10 |> subtract(4) |> subtract(3)

and we want to express that code over a list that's arbitrarily long, we we
must do this:

Enum.reduce([4, 3, ...], 10, fn x, acc -> subtract(acc, x) end)

to flip the two arguments to the correct place.

But we can't do so. I know the Elixir way is to generally put up with a
little ceremony for the common good of a tighter standard library, most of
the time, but I don't think this problem applies. I think that Elixir
*wants* this function:

Enum.fold([4, 3], 10, &subtract2)

To me the cost is high:

1. We must add a function to Enum, a central Elixir function
2. The function does almost the same thing as an existing function.

To me at least the benefit is higher:

Enum.fold encourages us to write better code in three ways:

A, Enum.fold encourages us to write code with T first, the Elixir way.

B, Enum.fold encourages named functions rather than anonymous functions
which leads to more opportunities to name concepts without comments, always
a good thing.

C. This strategy has a profound impact on tests of reducers, as we can
simply pipe them and check the results.

What do you think?
-bt








-- 

Regards,
Bruce Tate
CEO

<https://bowtie.mailbutler.io/tracking/hit/f8218219-d2a8-4de4-9fef-1cdde6e723f6/c7c97460-016e-45fb-a4ab-0a70318c7b97>

Groxio, LLC.
512.799.9366
[email protected]
grox.io

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/CAFXvW-7b0dc%2BOYYiGiYNvSzkY-tF-diSYLCH5DwVwGpi-cSvkQ%40mail.gmail.com.

Reply via email to