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.
