I accept that this is a widely used phrase now but I never understood why just using map/filter/reduce and avoiding state is enough for code to be called "functional programming".
Most functional languages feature pattern matching, algebraic data types, purity, currying, strong typing, type inference, recursion instead of loops and types that cannot be null. It's a completely different style of coding. Adding map/filter/reduce to an imperative language doesn't get you close to the level of robustness and ease of implementation for that safety you'd get in a functional language.
> Most functional languages feature pattern matching, algebraic data types, purity, currying, strong typing, type inference, recursion instead of loops and types that cannot be null.
It's only fairly recently that the set of functional programming languages for which that is even approximately true became the center of FP attention. For quite a long time, the flagship family of “functional programming” languages was the Lisp family, members of which for the most part feature none of those except possibly idiomatic recursion over iteration (though many Lisps include constructs that work like iterative loops, even though they may be implemented under the covers with recursion.)
"In computer science, functional programming is a programming paradigm — a style of building the structure and elements of computer programs — that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data."
To that end map-reduce fits and can be referred to as 'functional programming'. The other things you mention are just great language features, but not specific to functional languages. Imperative C++ also has type safety, Swift has non-null types, etc.
> The other things you mention are just great language features, but not specific to functional languages. Imperative C++ also has type safety, Swift has non-null types, etc.
All those features in combination are shared by the vast majority of languages called functional programming languages. They're pretty much the defining features of functional languages was my point. Other languages can have a subset of the features but it doesn't make them functional languages. See: https://wiki.haskell.org/Functional_programming
None of pattern matching, algebraic data types, strong typing, type inference and types that can't be null are specific to functional, nor do they make it any more functional. Purity isn't a language feature, it's a measure of how functional a language is, so it's tautological to say functional languages share purity. Currying is a byproduct of first class functions, not of functional programming, and currying is common in JavaScript and Python and other non-functional languages including C++. Forced recursion instead of loops would just be a byproduct of not having mutable state. And a good alternative to a recursive loop is a call to map() or reduce().
> The Wikipedia quote is really vague.
I think the Wikipedia quote is clearer and FAR more precise than a common feature set. Functional programming "treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data." That is the definition of functional, and it's the whole definition. No mutation, that's it. Including the features you wrote about is not functional programming, it's other things.
That's important to consider the precise definition if you're going to critique someone for talking about functional programming in a certain way. The author was pretty clear that this is functional programming concepts as applied to arrays in JavaScript, he never claimed that this post covers functional programming in general.
Map, filter & reduce are examples of functional programming, and if they were all you used, and you had only const and no var, then you might have a functional program. @zabana didn't claim that this is all you'll ever need.
> Functional programming "treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data." That is the definition of functional, and it's the whole definition. No mutation, that's it. Including the features you wrote about is not functional programming, it's other things.
Are you meaning that "no mutation" is the definition of functional programming? That's such a vague definition I can't see how it is useful personally. "Stateless" is already a word that covers that.
If you're going to quote Wikipedia, keep going to the "Concepts" section that lists language features:
"A number of concepts and paradigms are specific to functional programming, and generally foreign to imperative programming (including object-oriented programming). However, programming languages are often hybrids of several programming paradigms, so programmers using "mostly imperative" languages may have utilized some of these concepts.[40]"
> The author was pretty clear that this is functional programming concepts as applied to arrays in JavaScript, he never claimed that this post covers functional programming in general.
I even said I accept it's common usage of the phrase and I was just wondering why that was.
> Are you meaning that "no mutation" is the definition of functional programming?
You can see that I was intentionally summarizing wikipedia's definition in one word to make my point more clear that functional does not mean type inference or currying, right?
> That's such a vague definition I can't see how it is useful personally. "Stateless" is already a word that covers that.
Since I'm not offering "immutable" as the complete and final definition for functional programming, maybe you can find a better one to make your original point clear? Because the list of features you offered as defining functional programming doesn't define functional programming very well.
IMO, the entire first paragraph of Wikipedia's article on functional programming is pretty good. It repeats the concept of immutability in a bunch of different ways with clarifying examples, to make it more concrete and less vague.
I don't personally like "stateless" because it's not strictly true. Functional programs have state, and the state is contained in the stack. The point of functional programming is that the language avoids side effects, and the most direct word for something that avoids side effects is "immutable", not "stateless". I can accept that common usage of stateless is sometimes referring to immutability.
Note that WP's definition of "state" agrees with that, and talks about declarative state being indirect, as opposed to being stateless: "In declarative programming languages, the program describes the desired results and doesn't specify changes to the state directly."
> I even said I accept it's common usage of the phrase and I was just wondering why that was.
Because it's true and meets the definition? Your objection is too vague. What, exactly, is wrong with calling map/filter/reduce "functional"? In my book, referring to map as an example of functional programming doesn't stretch the strict definition of functional programming at all.
map() and reduce() were some of the very first things I learned about when I was introduced to the concept of functional programming in Scheme, more than 20 years ago.
The post didn't claim that map, filter & reduce were 'enough for code to be called "functional programming"'. He only ever implied that map, filter & reduce are part of functional programming. That is absolutely true, always has been, and isn't a matter of common usage.
Clojure uses dynamic typing, yet it is still considered a functional language. I agree though that the combination of features results in a better experience when using functional primitives, but not having them doesn't mean this isn't functional programming still.
I think most of it is the ease (demonstrated in the comment) with which "functional programming" elides into "functional languages." Functional programming is a technique. Programming languages are tools. Some tools generally facilitate functional programming techniques better than other tools.
Robust programs can be written in many languages, including 'many languages' in the sense of heterogeneity as demonstrated when a Haskell program communicates over the internet hopping through routers and switches running embedded and iOT and OS quality C code...and worse.
Functional programming techniques are great, until something else is needed. At some point databases become useful. At some point caches become important. At some point everyone winds up needing random numbers. Relying on statistics and probability isn't a bad idea either, right Siri and Cortana and Alexa?
> why just using map/filter/reduce and avoiding state is enough for code to be called "functional programming"
A better term would be "programming with functions as first class citizens", but many years ago that got shorted to "functional programming".
> Most functional languages feature pattern matching, algebraic data types, purity, currying, strong typing, type inference, recursion instead of loops and types that cannot be null.
That's a list of what many functional languages now contain, although most don't contain all of those. But that isn't the list of "common functional programming language features" from 10 years ago, and isn't necessarily what the list will be in 10 more years, either.
Well... for some of us, when you say "functional programming", you say "Haskell". And it certainly had all of those features, and much more, more than 20 years ago.
I really like another poster's recommendation that it be called "combinator-oriented programming". Because, while ES, Java, and even Rust, supports rich combinator-oriented programming, I wouldn't dare call any of them functional programming languages.
Though, one has to admit, with ES6 now even supporting elegant currying, coupled with a good functional programming library like Ramda, and immutable data structures (Immutable JS), ECMAScript comes pretty close if you really want it to.
The difference is, any code anywhere can "drop out of" the functional world, whereas a functional language like Haskell enforces functional purity throughout.
Good times to be a software engineer, nevertheless!
> Well... for some of us, when you say "functional programming", you say "Haskell".
Haskell is a single, functional programming language. It is not the definition of what a functional programming language is. If you mean Haskell, say Haskell. But don't try and redefine "functional programming" to mean "this one specific functional programming language that I happen to like".
Because (a) we don't have an unambiguous definition of what functional programming is and (b) we don't have a better term for what this style of programming is.
I guess it could be called combinator-oriented programming or similar.
Sounds more like "coding with map, reduce and filter" to me. It beats complex for-loops where mapping, reducing and filtering are all mixed in together but I wouldn't call it functional programming.
As mentioned in one of the comments, this is nowhere near the definition of functional programming.
> It beats complex for-loops
Nvm. I still prefer for loops in most cases, in some way it's easier to understand for me if I read it some months later. For example the reduce function I would write something like this:
getPlayersByCountry= ( footballPlayers= [], players= {} ) ->
for player in footballPlayers
if players.hasOwnProperty player.country
players[ player.country ].push player
return players
playersByCountry= getPlayersByCountry footballPlayers, {France: [], England: [], Spain: []}
What I mean is when you have a 200 line for loop that includes other for loops, "continue" statements, if statements and modifying state it gets really confusing quickly. If it's short it's probably going to be OK to understand anyway but if you can decompose a long for loop into several map, reduces and filters it's much easy to understand what's going on because it's more linear.
For that example, you should really have a generic "group by" function to use anyway that plays nice with your map, filter and reduce functions.
Even with a small loop like that there are potential problems. Should there be an 'else'? You might know because you wrote it, but the compiler/runtime doesn't, and neither do other programmers on your team without taking time to analyse your intention. Using map/reduce/filter/etc. is declarative and less likely to result in spurious errors.
That's the benefit of this declarative approach, it removes the relentless boilerplate, works solely with intent, and reduces cyclomatic complexity.
>but I never understood why just using map/filter/reduce and avoiding state is enough for code to be called "functional programming"
Maybe that's enough to count as first-order functional programming ?
The Wikipedia article on Spreadsheet [1] seems to suggest that even a spreadsheet counts as first-order functional program:
>The formula may rely on the value of other cells, but those cells are likewise restricted to user-entered data or formulas. There are no 'side effects' to calculating a formula: the only output is to display the calculated result inside its occupying cell. There is no natural mechanism for permanently modifying the contents of a cell unless the user manually modifies the cell's contents. In the context of programming languages, this yields a limited form of first-order functional programming.[2]
Because they are very straightforward and practical examples of functions as first class objects and higher-order functions, which are more fundamental to any definition of 'functional programming' than things like type inference or pattern matching.
Very true, and it reminds me of the push to use "for each" statements in Java instead of looping with an index. Really it's just another "how to avoid for loops" movement.
Author here: I wrote this article mainly as a note to self that I could refer to in the future and as an exercise to help me learn the concept and force me to structure my thoughts into a coherent blog post. I thought I'd share it for those of us who are more intermediate and would like to pick up a few tricks along the way. Thank you all for the positive (and not so positive feedback)
For learning the concepts, something that I found rather enlightening was writing map/filter/every (and I guess, some or any, and really just about any function that operates on a collection), -with- a reduce.
Then, write your own recursive implementation of reduce.
I found it really helpful in grokking what these things did, and more broadly improved my understanding and use of functional composition
I don't know if I'm delirious but I remember a few years ago when articles like this were very common on HN. Then all of a sudden it felt like everyone had "learnt" it and all that got upvoted was very complicated articles explaining some esoteric JS thing.
Now it seems we're back again with. Another example is the article explaining "this" which is on the front page again. It almost feels cyclic, like it's time for the new generation of programmers to learn the quirks.
It's probably an incorrect observation but it feels like the front page has developed with me. Like we all started on square 1, evolved together and now it's time to graduate and leave room for the new generation.
What's happening is that you're passing an object (hashmap / hashset) into a function that returns a filtering function, and that object is used inside the closure to track the dupes. It's still a pure function because even though you're mutating the passed in object, the filtering function is still deterministic and referentially transparent.
Array.prototype.sort destructively modifies the array, which is typically not considered a part of the functional paradigm. You could make a case for pragmatism if the array were purely local to the function (i.e. not passed into it), so that nothing outside the function would notice anything being mutated.
Author here, thanks for your feedback ! I didn't mention sort because I thought I (personally) rarely use it and wanted to provide examples of more widely used array methods (namely filter reduce and map).
JavaScript has an advantage on Python in this style, because Python lambdas are hideously ugly and JS anonymous functions are less hideously ugly. In Python it tends to be cleaner (and sometimes faster) to write list comprehensions instead of map() and filter(), especially since Python also has lazy generator comprehensions.
makes a single pass over the data and gives you a list, but
list(map(func, filter(cond, collection)))
makes two passes and requires some extra function calls.
I think the real reason is perceived legibility on the part of the Python core devs. I seem to recall a document from a while ago in which GvR himself came out in favor of list comprehension style. "Explicit is better than implicit".
You did not "totally misunderstand something": Immutability is a virtue in FP, you should write in this style. But it is only a "fundamental principle" and "core concept" in highly pure languages - and even Haskell allows mutable variables[1].
Sometimes you just need them, e.g. when handling potentially very large vectors in finite memory. CL's (sort ...) for example mutates the input sequence (or "destructively sorts" it [2]).
agreed, the Array.prototype.reduce could be accomplished in a far more readable way using a simple forEach. If the function was doing a `concat` instead of a `push`, it would make a lot more sense to me as a functional example, because then the author would actually be taking advantage of the way the accumulator result from the previous call is passed into the next call.
Great article, but questionable formatting: `return {name: player.name, age: player.age }`
It also seems wrong to mutate the accumulator in the reduce example, it would just be simpler to use `forEach` and use a variable defined outside if you're going to write code this way?
In his example Array.prototype.reduce, isn't this almost the same as just doing a foreach loop? I don't understand why doing this, it's pretty much the same number of lines.
The problem with this is that it generates two intermediate arrays in the maps, then effectively drops them in the reduce. This may be inefficient.
I wish that JavaScript's map/filter/reduce functions returned lazy iterators, like in Rust, so that code like this doesn't produce intermediate arrays. Does anyone know of a library that provides this?
Those intermediate iterations could be dropped by composing the map functions together (same with the reduce), if that really was a performance bottleneck.
With lazy iterators, there is the possibility of better composition of maps/filters/reduces. That's why I'm a fan of the lazy iterators approach -- otherwise the abstraction breaks too easily and it ends up looking like a for-loop anyway.
Or... you can do all of that in a 4-line loop, without any of the harm to readability or additional code-bloat that's especially important for websites.
That's the thing about functional programming. I personally don't remember the last time I had the need to write a foreach loop. Foreach loops even kind of feel _dirty_ to me.
All I really need now is Array.map, Array.reduce, Array.filter, Array.sort, Object.keys, Object.values.
Reduce is very powerful, you just have to get to know it.
I noticed you used let to instantiate variables. Should switch to const, and if really going functional, will begin to find few rare cases where let is needed.
this post introduced me to the arrow function ((https://stackoverflow.com/questions/24900875)), which i'd previously not heard of. evidently it was introduced in ECMAscript 6 but for engines that use JavaScript and older browsers (amazingly, they still exist) it won't work
TL;DR author provides an example of map, filter and reduce functions in JS.
I have been wondering lately what is the value of articles like this. They do not convey any meaningful idea, they do not offer any useful insight on underlying matters; why do people even write something like this, except to litter the Internet even more?
Anyone, who is distinctly familiar with functional programming or even just with the concept of higher order functions, surely knows those three basic primitives; those, who are not, would benefit much more from the basic concepts of first-class functions etc, than this random abrupt post.
Someone on the internet wrote something but you don't like it. The appropriate reaction is to be quiet and go read something else.
Slagging the author for giving something to the world for free doesn't help anyone and it's almost certainly going to discourage the sharing which everyone on the internet has benefited from.
From my comment below: "Thank you for this! I've been learning JS for React and .reduce, .every and .some are new to me! Super helpful"
I feel like learning is always easier in smaller bitesizes and the person wrote something very clear and digestible. Sounds like it wasn't aimed at more advanced programmers like yourself
The blog post has tremendous value to the right audience, no doubt about that. But it's worse than worthless for its actual target audience because the article makes those people feel like they're supposed to get it, and they probably feel shitty when they don't (I did). Even though they shouldn't. No one expects you to read that blog post and get it if you've never used Map or Reduce before. It's misleading and disheartening to the target audience. It's a pretty easy fix. Just change who you're talking to.
When I discovered Array.map, I already knew about map/filter/reduce. The reason it was a discovery is that the bulk of Javascript resources used for(){} loops and in those few places where map/filter/reduce were used, many implemented map/filter/reduce as library functions because Array.map etc were not always part of Javascript...I think Mozilla added them at some point based on how I found out about them.
When I first started learning this stuff, I started a blog. I made maybe 3 posts before giving up on it, thinking about how much I didn't know, how that everything I could write about at the time was probably covered elsewhere and better, etc.
Several years later I regret giving up.
A lot of writing is writing for yourself. Obvious benefits to that, one of which is hopefully you get better at communication, which is huge in life in general, not just a career as a dev on a team.
Another is learning through teaching or keeping a record. I've probably forgotten a bunch of things over the years that, had I written them in my blog I may remember, or at least know where to find the post instead of scouring the internet looking for another post by someone else that I used to helped me solve a problem.
Potential employers may like seeing a blog as well. You can tell someone you're passionate about x, but where's the proof of that passion? It may not be the most in depth blog like Dr. Axel Rauschmeyer's, but it'll still show what you know. And you never know who's reading, so you may help someone in the process.
The internet is already filled with "litter" or useless things. But if you're getting use out of it then that's the main point to focus on.
I have wanted to start a personal dev blog myself for quite a while, but each time I think of a suitable topic, I immediately recall an article or a post that is already better than anything I could write myself on the matter.
If you think you have something valuable or interesting or just funny to share, that either was never discussed or written about or you know you can do better, then you absolutely should blog about it and do not let haters like me stop you.
As for the original article:
* It should have been named just "JavaScript arrays and higher-order functions" (I get that using term functional programming is very catchy nowadays, but I am trying to talk about quality here, not clickbaitness)
* As it is aimed for people unfamiliar with the concept and coming from more imperative background, where first-class functions are either not present or not that popular or handy to work with, an introduction (even the most gentle one) would be very suitable
* Then the introduction of filter/map/reduce functions would be in place, ideally compared in place with imperative implementation of the same task
* The best way to make sure a reader understands how each of this functions works is to make them implement each one of them, even the naive implementation would suffice
* Finally, you could provide some reasonably unobvious usages of any of the functions, e.g. insertion sort done with fold-only.
> I have wanted to start a personal dev blog myself for quite a while, but each time I think of a suitable topic, I immediately recall an article or a post that is already better than anything I could write myself on the matter.
While it may not be valuable to the vast majority of people, it was probably valuable to the author at least.
The article is not ground breaking or even front page worthy, but that doesn't mean it's the litter of the Internet. At absolute worst it's an exercise in thinking about a subject and trying to explain it to others.
I'm not sure why you feel the need to bash this person. If you already know the concepts then go read something else. Instead you took the time to write this shitty comment.
This may not provide value to you and thats completely fine. This does however provide a lot of value to the author and to others.
Writing technical thoughts in a way that is simple and understandable is a skill and I think this author did a great job of that.
Also its the internet I can write posts for myself all day long and guess what? Ain't shit you can do about it. I'll keep littering the internet with these "random abrupt posts".
Here's an example of why it's useful for me to see articles like this on HN. I'm an engineering manager, and someone on my team recently joined who wasn't very familiar with these more functional styles. I offered to explain to him benefits of declarative styles like this vs imperative, but he said he learned better by reading. He asked if I had any articles that I'd recommend about the topic, and since I've seen this, I'll send it his way.
And yet here it is, upvoted, clicked, and commented on by enough people to make it to the front page of a highly competitive news aggregator. It's easy to forget what it's like to not know something I guess, but clearly there are enough beginners still out there who find this type of post useful.
I'm sorry to say that I completely agree. As a "here's how you do X in javascript" this is a great post. However, the conclusion clearly shows that this post was not aimed at people who already know how to use map and reduce: "Functional programming is a great tool and will drastically improve your productivity...."
It's like someone showing you multiplication and saying. See look how useful it is! See look how simple and easy it is to understand! I still don't know multiplication, or even addition for that matter.
Map and reduce are things you build up to by doing recursive functions over and over again. They are not something you learn from reading an article. Just like you can't read an article and suddenly know how to solve problems with long division.
This hardly covers any functional patterns that javascript is capable of. You can compose functions, avoid stack overflows in tail call recursion, use immutable values, etc. I've seen articles on pattern matching with Typescript or trampolining code but this is simple a descriptive look at three basic javascript functions. If you're interested in more of a functional approach in JS take a look at Immutable.js and/or https://pattern-matching-with-typescript.alabor.me/.
Most functional languages feature pattern matching, algebraic data types, purity, currying, strong typing, type inference, recursion instead of loops and types that cannot be null. It's a completely different style of coding. Adding map/filter/reduce to an imperative language doesn't get you close to the level of robustness and ease of implementation for that safety you'd get in a functional language.