mszmurlo

mszmurlo

In comprehensions, why a syntax difference between `into:` and `reduce:`

Recently ‘discovered’ the reduce: option for comprehensions. Very useful, yet there is a syntax difference between into: and reduce: that I don’t understand:

  • into: requires the comprehension’s code block to return the element that is to be inserted into the collection:

    for x<-0..2, y<-0..3, into: %{} do
      {{x, y}, x*y}
    end
    
  • reduce: requires a accumulator -> code block

    for x<-0..2, y<-0..3, reduce: %{} do 
      acc -> acc |> Map.put({x, y}, x*y)
    end
    

    This allows doing multiple modifications to the collections which actually was my use case:

    for x<-0..2, y<-0..3, reduce: %{} do 
      acc -> acc |> Map.put({x, y}, x*y) |> Map.put({x+10, y+10}, x*y)
    end
    

I was wondering why in the first case we simply return the element to be inserted while in the second case we need a acc -> code(acc) block. I believe it’s for having the possibility to name the accumulator, but is this assumption correct?

I could imagine that for into: we could return a tuple of all the inserts to be done:

for x<-0..2, y<-0..3, into: %{} do
  { {{x, y}, x*y}, {{x+10, y+10}, x*y} }
end

or for reduce: to have a syntax like reduce: {acc_name, acc_initial_value}:

for x<-0..2, y<-0..3, reduce: {acc, %{}} do 
  acc |> Map.put({x, y}, x*y) |> Map.put({x+10, y+10}, x*y)
end

(I actually never really thought about what acc -> some_code(acc) means. I simply use it because the documentations says so. So I may also need a quick explanation on this topic…)

Most Liked

mudasobwa

mudasobwa

Creator of Cure

Well, I am the one who uses for comprehension if and only if I need a filter (maybe several nested clauses as well, but I don’t realy do numeric calculus often.) Specifically, I love filtering with pattern matching, not the bare x < 0-like filters:

for %{status: :error} = error <- results,
    %{origin: :some_cause} <- [error],
  do: error.description

YMMV.

mudasobwa

mudasobwa

Creator of Cure

This question is isomorphic to the following one:

Why Enum.map has arity 2, but Enum.reduce has arity 3?

One might abuse Enum.reduce/3 for mapping, but the actual difference is reduce is folding, decreasing the dimension of the input. In SQL that’s called aggregation. sum is arguably the most trivial example, receiving a list and producing a scalar. That’s obviously impossible without the accumulator.

LostKobrakai

LostKobrakai

The reduce syntax mimics the syntax of an anonymous function fn parameter -> body end. The reduce option uses that, because it‘s the only option on for, which gets an extra input beyond what is provided by generators or assignments in the for … do portion - the first parameter to the for special form.

Last Post!

josevalim

josevalim

Creator of Elixir

Simplest way to think about it is that :into is a convenient option that builds on top of reduce. You give it a data type and Elixir adds the reduce loop associated to that data structure for you (which is done via the collectable protocol).

Where Next?

Popular in Thoughts On... Top

pera
Releases is a fantastic new feature in Elixir 1.9, but it comes with one important caveat: Once a release is assembled, it can be packa...
New
W3NDO
I went back to building on ruby during an interview and I was asked to explain symbols. It got me thinking about ruby symbols and elixir ...
New
mszmurlo
Recently ‘discovered’ the reduce: option for comprehensions. Very useful, yet there is a syntax difference between into: and reduce: that...
New
carlosescri
Hi all! I’ve been working on some RAW (no framework) web components for one of our projects to see how well they behave with Phoenix Liv...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44265 214
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

We're in Beta

About us Mission Statement