Some thoughts in no particular order:

- Generally, the Elixir stdlb does not support multiple ways of doing 
things, especially with operators (see: no Integer.add/2, etc). I generally 
like this.
- The other container that uses a < ... | ... > update syntax (maps) does 
have a dedicated function for this (Map.update). I also like this because 
piping Map operations is so common.
- After a decade of Elixir I still reach for List.concat and List.prepend 
every few months. I'm intimate with the operators and not an overzealous 
piper, but when piping I still expect it to be there. If I wrote Elixir 
exclusively then I would probably fully adjust.

> So I think that the implementation of this can just perform a "remap" of 
the ++ operator and the [e | list] expression.

I agree, in fact I would implement this as a compile-time inline 
<https://hexdocs.pm/elixir/Kernel.html#module-inlining>. (I believe just 
invoking :erlang 
<https://github.com/elixir-lang/elixir/blob/7b20c281d521aa7aa2ad2baa1e9ae6c579d79d0c/lib/elixir/lib/kernel.ex#L1590>
 
is insufficient, IIRC there is something going on in the .erl compiler as 
well to enable this performance characteristic. (Found it—here 
<https://github.com/elixir-lang/elixir/blob/41d1a721ad52e9fbc5f1770b74c3c1c31bccd2d0/lib/elixir/src/elixir_rewrite.erl#L89>
.))

> Similarly, it rarely occurred to me to use `[elem | list]`. I instead 
looked for `List.prepend/2`, then `Enum.prepend/2`. When neither of those 
existed, I had to resort to elixirforum/slack/stackoverflow/etc to lead me 
to the `[elem | list]` syntax.

One could argue this is working as intended. Preferentially, though, you 
would have first discovered the correct operators by consulting the List 
moduledocs when you could not find the expected function.

However, we could implement the expected functions here to 1) support the 
pipe use-case and 2) have an indexable and discoverable point in the 
documentation to direct folk to the operators, at least for concat and 
prepend. This is my preference. Implementing an append gives us another 
discoverable place to advise against it, so there's an argument there too I 
guess.
------------------------------
I also wonder if it would be possible to somehow attach metadata to the 
list operators so that they show up when searching for the expected 
function equivalent. The current situation is pretty poor for these 
operators.

Today, typeahead for "prepend" shows nothing related. A full search for 
"prepend" eventually shows something almost relevant in 11th place (the ++ 
docs telling you when to prefer [ | ]). The List moduledoc instructions for 
[ | ] show up in 15th place. There is no entry for the actual infix list 
concat operator in Kernel or SpecialForms as it is context-dependent. Map 
and tuple literals have entries in SpecialForms (%{} and {} respectively), 
but there is no similar entry for list literals ([]).

Similarly, there are no related results for ++ when searching "concat". 
"concatenation" brings up the relevant operator in 10th place. Neither turn 
up anything related in the typeahead.
------------------------------
Overall, I'm pro List.prepend/2 and List.concat/2. Documentation search 
aside, I think the friction of discovering the operators could be reduced 
by mentioning them in dedicated function docs, and alongside the 
pipe-usecase and parallels to the Map APIs, there is a sufficiently 
compelling case to be made to abandon the one-way-to-do-it principle here.

Conversely, reaching for List.append/2 should produce some form of friction 
and lead the programmer through a learning experience. Whether or not the 
"resort to elixirforum/slack/stackoverflow/etc" experience is a productive 
sort of friction, I don't know.

I think we should investigate improving the hexdocs situation for these 
operators regardless.

Finally, if these are implemented as inlined-at-compile-time to their 
operator forms, it occurs to me we could have the formatter auto-correct 
non-pipe usage to their operator forms. I'm a little wary of that, but 
could be convinced otherwise.
On Tuesday, May 27, 2025 at 5:16:30 PM UTC-5 Dallin Osmun wrote:

> This was a hurdle for me when I first started coding in Elixir. Whenever I 
> wanted to update a data structure, I'd look through the standard library 
> for the appropriate function.
>
> Instead of using `list1 ++ list2` I looked for `List.concat/2`. When that 
> didn't exist I'd look for and find `Enum.concat/2`. Similarly, it rarely 
> occurred to me to use `[elem | list]`. I instead looked for 
> `List.prepend/2`, then `Enum.prepend/2`. When neither of those existed, I 
> had to resort to elixirforum/slack/stackoverflow/etc to lead me to the 
> `[elem | list]` syntax.
>
> All that to say, I'm torn on this proposal. On the one hand it could help 
> those new to elixir get un-stuck faster. On the other hand, those same 
> coders wouldn't be pushed to learn the `[a | b]` syntax.
>
> On Saturday, May 24, 2025 at 1:21:22 AM UTC-6 sabi...@gmail.com wrote:
>
>> My concern is that adding List.append/2 would send the wrong signal, 
>> since it has the wrong performance characteristics.
>> It is a pattern that people coming from an imperative need to unlearn so 
>> we shouldn't make it more convenient.
>> We also just deprecated 
>> <https://hexdocs.pm/elixir/changelog.html#4-hard-deprecations> 
>> Tuple.append/2.
>>
>> While List.prepend/2 doesn't suffer this issue, I'm not sure the 
>> pipe-ability alone is enough to justify it, esp. since there is a 
>> first-class syntax for prepending: [h | t].
>> It feels to me that then/2 gives us the ability to use it with the pipe 
>> if we want to, with the flexibility of choosing what the first argument is:
>>
>> ... |> then(&[elem | &1])
>>
>> ... |> then(&[&1 | list])
>>
>>
>> Le sam. 17 mai 2025 à 07:24, Almir Neto <almir.a...@gmail.com> a écrit :
>>
>>> Today, if you want to pipe an append or a prepend, you must use then/2 
>>> to achieve this. This can be useful if the first elements of a list must be 
>>> inserted in order through a pipe. Let me show an example:
>>>
>>> dto.ledger.accounts
>>> |> Enum.reject(fn %{account_number: acc_number} ->
>>> acc_number in [debit_acc_number, credit_acc_number]
>>> end)
>>> |> then(fn accounts -> [get_ordered_debit_accounts() | accounts])
>>> |> then(fn accounts -> [get_ordered_credit_accounts() | accounts] end)
>>> |> then(fn accounts -> accounts ++ retrieve_unordered_accounts() end)
>>> |> then(&Map.replace(dto.ledger, :accounts, &1))
>>>
>>> Obviously, that one is too simple and can be done in one line without 
>>> losing too much readability, like this:
>>>
>>> dto.ledger.accounts
>>> |> Enum.reject(fn %{account_number: acc_number} ->
>>> acc_number in [debit_acc_number, credit_acc_number]
>>> end)
>>> |> then(fn accounts -> [get_ordered_debit_accounts(), 
>>> get_ordered_credit_accounts() | accounts])
>>> |> Kernel.++(retrieve_unordered_accounts())
>>> |> then(&Map.replace(dto.ledger, :accounts, &1))
>>>
>>> But it could be cleaner if it could just do this:
>>>
>>> dto.ledger.accounts
>>> |> Enum.reject(fn %{account_number: acc_number} ->
>>> acc_number in [debit_acc_number, credit_acc_number]
>>> end)
>>> |> List.prepend(get_ordered_debit_accounts())
>>> |> List.prepend(get_ordered_credit_accounts())
>>> |> List.append(retrieve_unordered_accounts())
>>> |> then(&Map.replace(dto.ledger, :accounts, &1))
>>>
>>> So I think that the implementation of this can just perform a "remap" of 
>>> the ++ operator and the [e | list] expression.
>>>
>>> def append(list, element) when is_list(list), do: list ++ [element]
>>> def prepend(list, element) when is_list(list), do: [element | list]
>>>
>>>
>>> This feature is more of a syntactic sugar than something innovative; it 
>>> would be a way to keep the code more vertical and easier to read for some.
>>>
>>> *I will be glad to send a PR.*
>>>
>>>
>>> -- 
>>> 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-co...@googlegroups.com.
>>> To view this discussion visit 
>>> https://groups.google.com/d/msgid/elixir-lang-core/ed3ccb00-5069-4b66-9d6f-132eadc7ea90n%40googlegroups.com
>>>  
>>> <https://groups.google.com/d/msgid/elixir-lang-core/ed3ccb00-5069-4b66-9d6f-132eadc7ea90n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>>

-- 
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 visit 
https://groups.google.com/d/msgid/elixir-lang-core/37c4788d-662d-4de4-98e1-06913db75665n%40googlegroups.com.

Reply via email to