I think my last message may have been too short, not explaining my 
reasoning well enough.

The main concern I have is that if you have a very flexible function whose 
set of internal operations depends on its arguments, 
it is no longer immediately obvious (i.e. obvious from only looking at the 
code where it is used) what the particular order of these operations will 
be.

For instance:

```
Enum.operation_by(names, :count, &String.downcase/1, &filter_fun/1)
```

When is `String.downcase` run here, and what impact will it have on the 
result?

If one were to write a longer pipeline with more, simpler, steps, then it 
becomes easier to read and comprehend.

For instance, we might have either

```
names
|> Enum.filter(&filter_fun/1)
|> Enum.map(&String.downcase/1)
|> Enum.count()
```

or

```
names
|> Enum.map(&String.downcase/1)
|> Enum.filter(&filter_fun/1)
|> Enum.count()
```

in each case resolving the ambiguity.

Of course, we're leaving some runtime efficiency on the table by having 
multiple smaller steps.

When this really becomes a problem, we can replace a particular pipeline 
with `for`:

`for` is a much higher-level abstraction than doing manual recursion, 
because:

   - It has its own generator syntax to filter on a pattern match.
   - Besides being also able to filter by functions.
   - It allows you to pass the extra options `:reduce`, `:into` and/or 
   `:uniq` to abstract away common transformations of the result.

Such a for-loop might be slightly harder to read than a pipeline of simple 
steps,
but it will be more performant because no intermediate enumerables are 
constructed any more.
And it will still be clear in which order the steps are performed.

Example:

```
for name <- names, 
      filter_fun.(String.downcase(name)),
      reduce: 0 do
      acc -> acc + 1
end
```

So this is why I am not currently convinced that this proposal is a good 
idea.

To summarize: In most cases the clarity of having more simpler steps in a 
pipeline is preferable, 
and in the cases where performance really does become critical, we already 
have `for`.

I hope that contextualizes my earlier reply a little :-)

~Qqwy/Marten
On Tuesday, January 12, 2021 at 11:20:52 PM UTC+1 [email protected] wrote:

> Well I didn't cook up a proposal, just had an idea and thought I could 
> write, at this moment I have nothing as a concrete proposal written in code.
> Looking through the API, I thought it could be applied to, for example, 
> map, zip, reduce, sum, take, into, shuffle, scan, basically anything that 
> goes through a list. 
>
> Il giorno martedì 12 gennaio 2021 alle 23:14:40 UTC+1 José Valim ha 
> scritto:
>
>>  
>>
>>> While I understand that there's no `Enum.filter` written explicitly in 
>>> the code, there's still this step of sort of filtering out, written one way 
>>> or the other, which at high level looks like filtering e.g. the simplest 
>>> example is this:
>>> ```
>>> def count(enumerable, fun) do
>>>   reduce(enumerable, 0, fn entry, acc ->
>>>     if(fun.(entry), do: acc + 1, else: acc)
>>>   end)
>>> end
>>> ```
>>>
>>
>> Can you please tell me which functions you believe this pattern could be 
>> applied at? You mentioned count and filter and other _by functions, but I 
>> am still struggling to see how it applies to max_by, dedup_by, chunk_by, 
>> etc.
>>
>> Otherwise, it is best to revisit it once there is a more concrete 
>> proposal (even if not efficient at first).
>>
>>

-- 
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/3e3c2dbc-393b-4535-9dd8-9fb1117c294bn%40googlegroups.com.

Reply via email to