mszmurlo

mszmurlo

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…)

Showing Posts 1 to 8

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.

gregvaughn

gregvaughn

Note, this is equivalent to

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

If you have a single clause for your accumulator, then you can likely find a way to convert to an into instead of reduce. Where reduce really shines is when you choose not to update the accumulator under some conditions. This implies multiple clauses. (And even some of those could use the filter feature of for but not many people are familiar or comfortable with for filters).

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.

gregvaughn

gregvaughn

Oh, but we do have Enum.reduce/2 also :grin: The first element of the enumerable is the initial accumulator which is only useful if your result is the same type as the elements of the enumerable, which arguably makes sum the poster child for Enum.reduce/2 :grin:

mudasobwa

mudasobwa

Creator of Cure

Technically, we don’t. It’s just a syntactic sugar. But yes, I get your point :slight_smile:

codeanpeace

codeanpeace

That’s how I think of it as well which reflects why it’s named “reduce”

Funnily enough though, OP’s example is using reduce to unfold and increase the dimension so whereas mapping is n to n, reducing is n to any though typically less than n ¯\_(ツ)_/¯

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).

— All posts loaded —

Where Next? Top

Trending in Thoughts On... Top

EssenceOfChaos
I created an app that uses Phoenix LiveView and is deployed on Gigalixir. The app manages the state of multiple players (for example, a ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews