Looking back, I think having acc as the first argument would be have a better choice. But we have other functions named after "fold" such as List.foldl/3, List.foldr/3, Inspect.Algebra.fold_doc/2 that take a function in the same format as Enum.reduce/3. So having some fold functions that take a function in the (current_acc, element -> updated_acc) format and others in the (element, current_acc -> updated_acc) would be hard to remember which one is which. I guess this proposal should consider a different name for the new function.
On Wed, 9 Dec 2020 09:40:35 -0500 Bruce Tate <br...@grox.io> wrote: > 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 > -- 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 elixir-lang-core+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/5fd12f36.1c69fb81.6e0da.1dc6SMTPIN_ADDED_MISSING%40gmr-mx.google.com.