[Α] Using forEach as a diving board to understand Functional Programming

Loïc TRUCHOT
Jul 21 · 7 min read

We control complexity by building abstractions that hide details when appropriate.

– SICP, 1979

Photo by JR Korpa on Unsplash

We JavaScript coders have the chance to practice a multi-paradigm language supporting functional programming.

Do you want to learn that power?

Let’s start with 24 lessons, from Α to Ω, solving our everyday problems. Lessons overview: α. forEach, β. map, γ.filter.


A Simple List Iteration

Consider the following data: a list of strings.

JavaScript is not strongly typed, so a collection bigger than langs could be buggy. Let’s pretend we care, iterate on it, and check the console to see what we get there.

We learned how to do that since day one of our programming journey.

For us, it looks like very intelligible code. But is it really?

It’s an imperative approach, since it describes how the computer should iterate, not how humans think about iteration.

It’s not as imperative as an if combined with a goto, but still.

And this code contains several other inherent defects:

  • It’s not reusable / not DRY (we have to rewrite all of this for every similar case).
  • Not decomposable, re-composable, since it’s a block of procedural code
  • Not easily maintainable: typos can hide here, and what if something changes the value of i during the loop? It could lead to bugs.
  • Not very scalable, since nested for loops drive programmers crazy

Syntactic Sugar

Thanks to coder-friendly ES6, we now have some syntactic sugar to write it without declaring an extra variable as counter.

OK, it’s more declarative: a non-programmer might understand this code, just reading it left to right.

A little more maintainable and scalable too… but for a few levels of complexity only.


Old but Gold

Five years before ES6, JavaScript already specified a useful tool: forEach.

forEach is not a native control structure, nor a reserved keyword. It’s an array method, executable through any Array, even an empty one.

The previous example will lead to an error, because forEach takes one argument¹.

But not a simple, random argument: it takes a function as an argument.

Remember that in JS, functions are first-class citizens.

This doesn’t mean functions are something special — in fact, it’s the opposite. In JS, functions are like every other value (objects, or primitives boolean, number & string): we can put them in variables, combine them, pass them as arguments, return them, etc. (In Haskell, even operators like + can be passed as arguments!)

forEach is what functional programmers call a higher-order function. Nothing complicated either; it’s just a function responsible for running or returning other functions.

So forEach is not magic; it takes a declared function (not executed!), but with the following signature: (any) -> void². And it will apply this given function successively to every element in the array.

This any argument stands to replace the current element in the iteration, and void tell us that this function shouldn’t return anything.

The previous code will simply pop up an alert saying “hi <language>” for each language in our array. So now we can use what we know to solve our problem.

But wait.

Is console.log a function? Yes.

Does console.log have an (any) -> void signature? Kind of.³

So what’s the difference between

and simply

?

Can’t see any difference: the wrapping function is just noise pollution, so …⁴

Oh … yes … sounds weird, but … clearer in some way.

Is it… that? Are we doing real FP now?

No, it’s just the #A of this “from A to Ω” collection. The real FP world is very strict because it tends to only allow provable programs and controlled side effects. And if you want to reach this ideal, you have to follow some rules.


Pure Function and Immutability

If you don’t understand everything that follows, don’t panic. We will dive into all of this, episode by episode. Just try to familiarize yourself with the contents.

Do you remember the signature of the function that we can pass to the forEach method? It’s a function that returns nothing (so-called “undefined” in JS), or void in a typed world. And forEach itself is a method that returns undefined too.

This is the obvious symptom of functions with side effects. If it returns nothing, it should do something that affects the program elsewhere, before the return statement. Like changing some value of a global variable, or altering the array itself.

Something that one day we’ll forget. And it will surely lead to bugs. In conclusion, it’s not under control, while Functional Programmers are control freaks. They always want to know what they’re doing and even why.

forEach is way more composable than a for of since it takes a custom function, but still deeply chained to Array. We can’t easily pass the forEach itself to another function.

Anyway, composability and scalability are not achieved either.

So we already know that we have to define our own forEach if we really want to enter the FP world.

Let’s begin simply by hiding implementation details in a brand new function:

But…! It’s fully imperative! You lied to us, traitor!

Mmmh, I never said how the native Array.prototype.forEach JS method was implemented.

Yes, maybe I lied by omission. But we now have our own forEach, and once it’s fully written, we’ll never dive into this function anymore. It will be the most perfect forEach ever, and we’ll use it, as it is, in 30 years. Functions are just about hiding imperative details to give us a declarative tool.

No one expects you to forget all the knowledge you already learned in computer science, quite the opposite!

Even so, there is a way to iterate without using a for. It’s called recursion, but that’s for later, and without TCO it can lead to new problems.⁵

Here’s an example if you want to get your first sight of it.

Let’s finish our real-world forEach, to make a pure function. We will discover what exactly it is later. But for now, just remember that a pure function:

  • should return something
  • should not modify anything outside its scope, even the references given as arguments⁶

Wow. This forEach seems really better to my eyes, used to find and destroy side effects.

But, it clearly doesn’t solve our maintainability/scalability problem entirely.

Recently, a friend wanted to argue about FP. He understood everything about the requirements of pure functions (control, TDD, reusability, etc.), but he regretted the requirement of immutability. Let me ask a question: if all functions are pure and your program is only made of those functions, when will you face any mutability needs? Never. Pure function, aka referential transparency, is just the same concept as immutability, seen from another angle.


Currying

So let’s invoke another useful tool from the FP world: currying.

Because of what we said about functions as first-class citizens (just like any other values), we know that a function can return a function, enclosing parent variables values in it.

So we can finally write a very reusable and composable forEach:

Note the new order of the forEach arguments:

  • func, that will be partially applied
  • then the array arr

This new order allows us to create and reuse logEach or even doubleAndLogEach. In the future, it will help us to compose/pipe forEach with other functions.

Since in JavaScript, objects are passed by reference⁶, our forEach still has a big flaw: an array containing objects still exposed to mutations.

JavaScript is not statically nor strongly typed, so we can keep passing impure functions and even bad data types to our forEach (for that particular problem, using an alternative like TypeScript or Elm could be the only choice).

So we didn’t reach the promised perfection: can you propose your perfect forEach, in the comments?

A last word, just to get you angry after all of this. You may have discovered it reading this article: the concept of forEach itself is not FP oriented. It does imply side effects in its own definition. That’s why our holy trinity will not contain forEach, but only map, filter, and reduce, as you will see in the next articles.

Thank you for your time!

[¹]: In fact, it can optionally take a second argument: a contextual value for this.

[²]: The real one is more like (any, number, any[]) -> void, where the number is implicitly the current index, and where any[] is the calling array. Those other args can be useful if you need to know if the current element is last in the array, for example.

[³]: Except that the real console.log signature accepts an unlimited number of optional args, separated by commas, and then returns false (any, …any[]) -> false.

[⁴]: In fact, because of the signature difference, console.log will just show more (see [²] and [³] and jump to conclusion ^^).

[⁵]: Tail Call Optimization is a prerequisite to use recursion in place of for loops. Using recursion, the call stack could grow too much until the end case, and the call stack is limited. TCO doesn’t exist in JavaScript (except in Node 6), so we are stuck with those loops… for now.

[⁶]: In JavaScript, Objects (including Arrays) are always shared by reference, never copied. If you feel uncertain about that, you must spend some time to explore that.

Better Programming

Advice for programmers.

Thanks to Zack Shapiro

Loïc TRUCHOT

Written by

JavaScript Senior Programmer and Teacher

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade