Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

[Proposal] Add :join to comprehensions

120 views
Skip to first unread message

Christian Blavier

unread,
Jun 21, 2022, 12:15:16 PM6/21/22
to elixir-lang-core

Do you think a join option would be a good idea for comprehensions?
Something like Enum.map_join/2 that could map and join lists in a single pass.

book_ids = for book <- books, join: ",", do: book.id

If it looks like a good idea, I will open a PR.

Best,

Christian

José Valim

unread,
Jun 21, 2022, 12:40:46 PM6/21/22
to elixir-lang-core
It should probably be done with using `into: joiner(",")`, where the joiner implements the collectable protocol. join itself probably doesn't make much sense because it doesn't play well with other options such as :into itself, or :reduce.

--
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 on the web visit https://groups.google.com/d/msgid/elixir-lang-core/f8b94dec-35a1-4b78-a14c-6913c7206fe3n%40googlegroups.com.

Christian Blavier

unread,
Nov 15, 2024, 5:06:06 AM11/15/24
to elixir-lang-core
Hey there, 

I'm still thinking about extending comprehension. What about a `then` option?

for i <- 1..3, do: i, then: &Enum.sum/1

for s <- ~w(a b c), do: s, then: &Enum.join(&1, ", ")

(I know this can be achieved more efficiently with a reduce, but sometimes concise syntax is a better choice)


José Valim

unread,
Nov 15, 2024, 5:10:56 AM11/15/24
to elixir-l...@googlegroups.com
It is the same answer as before, I am afraid. The path to adding :join is also the path to adding :intersperse, and :sum, and :product, etc. We should have a general mechanism for expressing those, instead of individual options.

And, as before, a joiner does not really work well with into and reduce.

Christian Blavier

unread,
Nov 15, 2024, 5:13:45 AM11/15/24
to elixir-l...@googlegroups.com
My point wasn’t about adding a join but a then option (like Kernel.then), which seems much more versatile. 

Whatever I was expecting a no :)
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/tF2Xw8nq-O0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4JEM%3DUD70e%2BpNXyKQqNxMB_gZnMbj%2By8ZH7tTr6rO5ahQ%40mail.gmail.com.

José Valim

unread,
Nov 15, 2024, 6:42:26 AM11/15/24
to elixir-l...@googlegroups.com
But then why not simply pipe to the result afterwards? This is all possible today already:

for(...) |> Enum.join(", ")
for(...) |> then(&Enum.join(&1, ", "))

for do
  ...
end |> then(&Enum.join(&1, ", "))

Christian Blavier

unread,
Nov 15, 2024, 9:16:04 AM11/15/24
to elixir-l...@googlegroups.com
What I'm proposing, is only syntaxic sugar.

I don't like piping comprehension's result. It looks less readable than 
piping with regular Enum functions.

Daniel Kukula

unread,
Nov 16, 2024, 4:59:52 AM11/16/24
to elixir-lang-core
I have also one proposal to for loops:
reverse: true
for applies already the reverse and when I don't care about the order, or want to reverse the result anyway then it has to be reversed twice.
It would be a nice addition that could add a small bit of performance in some cases. Also it should not conflict with existing features?.

José Valim

unread,
Nov 16, 2024, 5:29:00 AM11/16/24
to elixir-l...@googlegroups.com
It does conflict in the same way. What does this mean: "into: %{}, reverse: true"? Or "into: <<>>, reverse: true"? Do you reverse bytes or graphemes? "reduce: ..., reverse: true" also only works if you reduce into a list, which is rarely the case.

The key aspect to keep in mind is that "into: ..." already gives options to do all of this. You could have a "ReverseList" collectable, a "Joiner" collectable, or "ReverseBytes".

And "for" is already complex enough, I don't think we should add options such as `then: ...` which is syntax sugar to an already existing construct. 

As far as I know, the constructs you cannot do today using either into or reduce is zipping, or anything that aborts early (reduce_while or take/drop), which would be a break in other programming languages.

José Valim

unread,
Nov 16, 2024, 5:36:58 AM11/16/24
to elixir-l...@googlegroups.com
Even the "map_reduce: ..." option I proposed a couple times, and it was not accepted, can be implemented today, although it is quite more verbose than simply piping the result:

# double each element and compute the sum at once
{mapped, reduce} =
  for x <- collection, reduce: {[], 0} do
    {list, acc} -> {[x * 2 | list], acc + x}
  end

{Enum.reverse(mapped), reduce}
Reply all
Reply to author
Forward
0 new messages