And we can’t forget about https://hexdocs.pm/elixir/List.html#insert_at/3
On Thursday, June 5, 2025 at 9:26:16 AM UTC+2 benjamin...@gmail.com wrote:

> [el|list] is pipeable as well, and if you are working with Erlang 
> strings/binaries you can use 
> https://www.erlang.org/doc/apps/stdlib/string.html#pad/4
>
> I think it’s important not to stare us self blind on a specific syntax. 
> All the functions there are currently available helps highlighting that the 
> lists we have in the BEAM are double linked list. 
>
> When I first read this thread all I could think about was that it could 
> create more confusion for new developers who might believe that our lists 
> are arrays. 
>
> Although you can easily write your own macro or functions to deal with 
> this, I don’t see the value in adding it to stdlib at this point. However, 
> I do see value in expanding the documentation with examples on what to do 
> in situations like these as they are excellent cases.
>
> On Wednesday, June 4, 2025 at 11:15:47 PM UTC+2 
> william.l...@cargosense.com wrote:
>
>> benjamin...@gmail.com good point about `:lists.append/2` being 
>> available. But I don't see how you can prepend with `:lists.flatten/2` in a 
>> way that's pipe-able. Can you show an example?
>>
>>
>> On Wed, Jun 4, 2025 at 4:53 PM benjamin...@gmail.com <
>> benjamin...@gmail.com> wrote:
>>
>>> You can pipe into append and prepend today with 
>>> https://www.erlang.org/doc/apps/stdlib/lists.html#append/2 and 
>>> https://www.erlang.org/doc/apps/stdlib/lists.html#flatten/2
>>>
>>> This feels like more of a documentation issue than something missing in 
>>> the stdlib.
>>>
>>> On Monday, June 2, 2025 at 10:17:37 PM UTC+2 william.l...@cargosense.com 
>>> wrote:
>>>
>>>> I'm for adding `List` functions that make piping easier and help with 
>>>> discoverability. Though I agree they won't often be the best choice for 
>>>> the 
>>>> reasons discussed.
>>>>
>>>> I will note that I think there are actually 4 operations that need to 
>>>> be considered if the goal is pipe-ergonomics (names TBD):
>>>>
>>>> defmodule List do
>>>> # For a new single element
>>>> def prepend(list, x), do: [x | list]
>>>> def append(list, x), do: list ++ [x]
>>>> # For a new list
>>>> def prepend_list(list, new_list), do: new_list ++ list
>>>> def append_list(list, new_list), do: list ++ new_list
>>>> end
>>>>
>>>> I think a clear distinction between functions that take an element and 
>>>> functions that take a list is crucial. In this thread I saw a few 
>>>> instances 
>>>> where `x ++ list` was needed but `[x | list]` was used instead, which I 
>>>> think highlights that the mistake is easy to make.
>>>>
>>>> As for needing both `prepend_list` and `append_list`, making `++` 
>>>> pipe-able is only convenient if both argument orders are available. 
>>>> Otherwise, you have to drop back into intermediate variables or `then` to 
>>>> get a `prepend_list`.
>>>>
>>>> I'll also note that I'm at like a 6/10 on this feature: I'm on the 
>>>> pro-side of the fence but not by a lot.
>>>>
>>>> On Fri, May 30, 2025 at 6:35 PM Christopher Keele <christ...@gmail.com> 
>>>> wrote:
>>>>
>>>>> > We do have Enum.concat/2 and typically we don't repeat functions in 
>>>>> the Enum module within the List module.
>>>>>
>>>>> I would argue that it wouldn't be a repetition as there would be a 
>>>>> material difference between them: List.concat would obey ++ semantics, 
>>>>> meaning 1) it would only work with actual lists as a first argument 
>>>>> rather 
>>>>> than any Enumberable and runtime error otherwise, but hopefully be caught 
>>>>> by typing systems; and 2) allow construction of improper lists via 
>>>>> non-list 
>>>>> non-Enumberables in the second argument. Sometimes that's desired 
>>>>> behaviour 
>>>>> but it does feel footgunny now that I say it, though. Certainly something 
>>>>> that would need to be outlined in the function docs if implemented, it 
>>>>> does 
>>>>> give me some pause.
>>>>>
>>>>> > My biggest concern is that I don't think piping to prepend leads to 
>>>>> easier to read code here, because you have to reverse the order in your 
>>>>> head (the order you read the lines is the opposite of the order it will 
>>>>> appear in the list).
>>>>>
>>>>> I agree the provided example can be confusing way to model many 
>>>>> solutions, and may be an indicator of a need for a simpler refactor. But 
>>>>> I 
>>>>> think your point is orthogonal to the List.prepend discussion—the same 
>>>>> problem exists when chaining [ | ] through intermediate variables. It's a 
>>>>> (valid) argument against prepending at all, not against this API. For 
>>>>> example, an experienced Elixirist will know they'll need to pipe the 
>>>>> result 
>>>>> of a recursive defp-function-implemented reduce into a :lists.reverse at 
>>>>> the end, regardless of the API they used to prepend along the way—and 
>>>>> sometimes piping into a prepend would save noisy intermediate variables 
>>>>> along the way. I do not see the availability of List.prepend greatly 
>>>>> influencing developers to build backwards lists more often when 
>>>>> inappropriate, but we may disagree there.
>>>>>
>>>>> FWIW my usecase is almost never chaining multiple prepends—usually I 
>>>>> reach for it as a finisher when map/reducing a series of transforms on a 
>>>>> list of dynamic values, and adding a special case/hardcoded value at the 
>>>>> end; same with the proposed List.concat. The ergonomics of the pipe 
>>>>> operator truly shine in these map/reduces, so it can be painful to have 
>>>>> to 
>>>>> create an intermediate variable just to use the literal operators to 
>>>>> conclude building the desired list. As an example, mapping over a 
>>>>> database 
>>>>> table to create options for a select, and wanting to prepend the null 
>>>>> option that is not modeled in the source data set.
>>>>>
>>>>>
>>>>> On Friday, May 30, 2025 at 4:47:40 PM UTC-5 José Valim wrote:
>>>>>
>>>>>> We do have Enum.concat/2 and typically we don't repeat functions in 
>>>>>> the Enum module within the List module.
>>>>>>
>>>>>> My biggest concern is that I don't think piping to prepend leads to 
>>>>>> easier to read code here, because you have to reverse the order in your 
>>>>>> head (the order you read the lines is the opposite of the order it will 
>>>>>> appear in the list)
>>>>>>
>>>>>> Given this code:
>>>>>>
>>>>>>
>>>>>>     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))
>>>>>>
>>>>>> I would rather write (keeping roughly the same structure to make it 
>>>>>> easier to compare):
>>>>>>     
>>>>>>     accounts = 
>>>>>>       Enum.reject(dto.ledger.accounts, fn %{account_number: 
>>>>>> acc_number} ->
>>>>>>
>>>>>>         acc_number in [debit_acc_number, credit_acc_number]
>>>>>>       end)
>>>>>>
>>>>>>     accounts = [get_ordered_credit_accounts(), 
>>>>>> get_ordered_debit_accounts() 
>>>>>> | accounts]
>>>>>>     Map.replace(dto.ledger, :accounts, accounts ++ 
>>>>>> retrieve_unordered_accounts())
>>>>>>
>>>>>> Especially if I am calling functions with *ordered* in the name.    
>>>>>>
>>>>>>
>>>>>> *José Valimhttps://dashbit.co/ <https://dashbit.co/>*
>>>>>>
>>>>>>
>>>>>> On Fri, May 30, 2025 at 11:34 PM Christopher Keele <
>>>>>> christ...@gmail.com> wrote:
>>>>>>
>>>>>>> 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-co...@googlegroups.com.
>>>>>>>
>>>>>> To view this discussion visit 
>>>>>>> https://groups.google.com/d/msgid/elixir-lang-core/37c4788d-662d-4de4-98e1-06913db75665n%40googlegroups.com
>>>>>>>  
>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/37c4788d-662d-4de4-98e1-06913db75665n%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-co...@googlegroups.com.
>>>>>
>>>> To view this discussion visit 
>>>>> https://groups.google.com/d/msgid/elixir-lang-core/ef1c181b-513f-40ac-9f17-24cdfbb453b5n%40googlegroups.com
>>>>>  
>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/ef1c181b-513f-40ac-9f17-24cdfbb453b5n%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-co...@googlegroups.com.
>>>
>> To view this discussion visit 
>>> https://groups.google.com/d/msgid/elixir-lang-core/db899a13-2a01-4d0a-bd06-881541d51d4bn%40googlegroups.com
>>>  
>>> <https://groups.google.com/d/msgid/elixir-lang-core/db899a13-2a01-4d0a-bd06-881541d51d4bn%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/97613496-335d-4a8b-b0bc-d34b65bdfb82n%40googlegroups.com.

Reply via email to