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

preciz
I had a lot of back and forth with Codex CLI &amp; GPT 5.6 Sol to use a SWAR optimization for URI.encode_www_form/1. I now have a versio...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews