Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It's generally the opinion of the Go community that map, reduce and filter are bad ideas due to how easily they are abused. A for loop gets the job done easily enough. If you've ever worked with data scientists working with Python, you'll quite often see them all chained together, probably with some other list comprehensions thrown in until it becomes one incomprehensible line.


If that's the case, then the Go community is wrong.

For loops do not get the job done easily enough. I've lost count of the number of times I've had to do contortions in order to count backwards inclusive down to zero with an unsigned int. With a proper iterator API, it's trivial.

Furthermore, for loops are a pain to optimize. They encourage use of indices everywhere, which results in heroic efforts needed to eliminate bounds checks, effort that is largely unnecessary with higher level iterators. Detecting the loop trip count is a pain, because the loop test is reevaluated over and over, and the syntax encourages complicated loop tests (for example, fetching the length of a vector over and over instead of caching it). For loop trip count detection is one of the major reasons why signed overflow is undefined in C, and it's a completely self-inflicted wound.

I'm generally of the opinion nowadays that adding a C-style for loop to a language is a design mistake.


It's also strange that a language that wants to encourage parallelism requires for loops, and specifies that they always run sequentially. Java 8 can put map/reduce with a thread pool in the standard library precisely because it doesn't use a for loop; imagine how tedious and repetitive the Go version would be: https://docs.oracle.com/javase/tutorial/collections/streams/...


To be fair, I don't think Go prioritizes parallelism as much as it does concurrency.


> For loops do not get the job done easily enough.

There was a nice paper at this years POPL which (in my opinion) allows you to substantiate this claim.

The paper is "Stream Fusion to Completeness", by Oleg Kiselyov, Aggelos Biboudis, Nick Palladinos, and Yannis Smaragdakis. The actual question in the paper is how to compile away stream operations to imperative programs using for/while loops and temporary variables. On the other hand, if we look at the output of the compiler we can see how complex it is to express natural stream programs using only for/while loops.

For instance, even very simple combinations of map/fold/filter create while loops and make it difficult to detect the loop trip count afterwards (in fact, you need an analysis just to detect that the loop terminates). If you combine several such streams with zip, the resulting code makes a nice entry for obfuscated code contests. Finally, if you use flatMap the resulting control flow doesn't fit into a for/while loop at all...

So for/while loops are not simpler than stream processing primitives and unless you explicitly introduce temporaries (i.e., inline the definition of map/fold/etc.) you quickly end up with very complicated control flow.


This all depends on what "the job" being done well enough is. You can't use a research paper to refute the experience of the many programmers who successfully use for loops to get their work done. That's a statement about usability, not expressiveness.

If you want to show something else is easier to use, you'd have to do a user study, and even that's not going to be universally applicable since it depends on the previous experiences of the user population being tested. It's why these things tends to be debated as a matter of taste.


Interestingly there are user study, mainly on kids.

They all show that the natural expression is through declarative programming and that for loop are never what comes naturally.


Interesting. Links?


Yes, people can get crazy with inline anonymous function chaining/composition, and that can quickly get out of hand in terms of maintainability and readability, but deeply nested imperative loops is often much, much worse to debug and understand, because the intermediate steps are not nearly as explicit as in a functional chain/composition that simply takes data and returns data at every step.

Regardless, these are simply cases of people writing bad code, and nobody is claiming map/reduce/filter is a panacea for bad code.

Functional composition/chaining works best with small, well-named single purpose functions that compose/chain together into more complex functionality (with appropriate names at every non-trivial level of chaining/composition). You can't easily compose/chain imperative loops this way (at least not without wrapping them in functions that take data, and returns transformed data, by which point you might as well use map/reduce/filter to transform the data to begin with to get rid of the impedance mismatch).


> If you've ever worked with data scientists working with Python, you'll quite often see them all chained together, probably with some other list comprehensions thrown in until it becomes one incomprehensible line.

It's not incomprehensible, it's just phrased a different way from what you're used to.

A lot of programming boils down to mutating local or global state, by executing lines of code -- each of which mutating the contents of some variable somewhere -- in order, one at a time. But this is just one style of programming, and is not the only one. When using map, filter, reduce etc. we, instead, construct a path through which data flows, where each line modifies some aspect of the data, as it flows through the path. This is essentially what Functional Reactive Programming is, although you can get the same effect using a pure language and function composition.


> It's not incomprehensible, it's just phrased a different way from what you're used to.

What would be incomprehensible?


Code that's badly written.

As opposed to code that just uses chained map, reduce etc as opposed to nested or sequential for loops.

One is a difference is skill, clarity etc, the other is a difference in style (imperative vs fp).


> It's generally the opinion of the Go community that map, reduce and filter are bad ideas due to how easily they are abused.

There is a germ of truth here - sometimes list comprehensions make code harder to understand than if you just wrote an if statement or a loop - but you've overgeneralized. I don't think anyone on the core Go team seriously thinks that map and reduce are bad ideas when Google relies so heavily on MapReduce (and its successors).


"Ability to abuse" isn't a good criteria for language design. I've seen plenty of abuse of Go channels, but I'm not going to make the argument you should remove them.

Python's comprehensions are among the most powerful, easy to use, concise capabilities of any language I've used. You can address the abuse issue with coding guidelines: no more than 2 deep. Done.


The same argument applies, mutatis mutandis, to imperative iteration constructs such as range.


>It's generally the opinion of the Go community that map, reduce and filter are bad ideas due to how easily they are abused.

How easily are they abused?

Because it's the opinion of the programming community, including the brightest programmers out there, that FP, and map, reduce and filter are totally fine and dandy.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: