A Gamified Approach to Learn Functional Programming
JavaScript without falling asleep while reading

Intro
Your world of JavaScript is in magical warfare. Also, your codebase is already possessed by evil powers. You may be a mad coding warlock, but anyway, you begin to lose more and more control over your code. The situation seems to be hopeless.
But there is hope. Nerdy mathematician virgins have found a construction plan for an ancient magic weapon. A weapon that has the power to turn the tide. The plan contains only one line and comes with a short note.

Build little baby functions. Each of them has a single responsibility. Each of them needs to receive an input and return an output. Put them together like Lego pieces, so a function output becomes the input for the following function. This will rescue you. This is what you need to put all your focus on. This is the essence of Functional Programming. This is called Composition.
But be aware: If your baby function is generating a side effect, for example, by mutating state. Your weapon will explode, and your application will crash into everlasting damnation.
You read through the note and the construction plan a few times and ask yourself: lol… that shouldn’t be hard at all. You take your wand… Uhm… I mean your mechanical LED keyboard, ramp up your IDE, and your journey begins… good luck, brave wizard.
About this Game
Who I am? Christoph, and hell no: I’m in no way a level 9000 Super-Saiyajin pro-level coder. I am just a random web dev expanding his skill- and knowledge-set, to become a real software engineer someday. So please, be gentle to me.
Why this article? In the last weeks, I’ve learned as much as possible about Functional Programming in JavaScript. Summarizing what I’ve learned about Functional Programming will help me to solidify my understanding of this topic. Also, I’m pretty sure that junior to regular devs will benefit from a not too professional perspective. The pros in the Functional Programming area tend to explain way more mathematically and nerdy. I will do the opposite in this story to prevent your brain and mine from exploding. Oh, and… I’ve made a deal with my boss to publish a Medium article once a month. So, hey boss: Here it is.
This story is for whom? This story could be interesting for you
- If you are completely new to Functional Programming or programming at all and need a mental model or;
- if you want to get a different perspective on some of its principles or;
- if you have a junior dev to whom you like to forward a mental model for the topic or;
- if you like weird fantasy stuff.
Stage 1: Baby Functions Wastelands
Immediately after your IDE opens, you get attacked by this spaghetti demon function:

You tried to close your eyes and scroll further, but it was too late. Without any warning, it starts to suck you in its weird data flow. You have seen the demonAttacks()
, and immediately you can feel how it pulls you down the rabbit hole of nested function invocations. You check what happens inside checkInventory
. You then follow playerEatsBananas()
and, before you realize it, you have lost your first life. You starved while going deeper and deeper following the data flow.

Nooooooooo…. okay keep calm and go back to the last Savepoint. And now, concentrate on your mission. To build your superweapon, you first need to construct small baby functions, functions that are made for only one super simple task. The simpler, the better.
So you glimpse at only the first few lines of our rabbit hole code and rewrite the stuff as new baby functions with single responsibilities. You also give them a name according to what they do. Just forget about the function invocations for now.

But something is still wrong here. You can feel it in your nipples. Arrrgh…. the return statements! Oh, noes, they are missing altogether. And also, it’s still a weird data flow. Ok, ok, ok.. you got that right just in time before it gets dangerous again.

Boooom!!! Your second life is gone. Sorry bro, you are still generating side effects by mutating the global state playerHealth
, which inevitably leads to final annihilation. Damn you, stupid state mutation from hell. Ohh, and btw. you are also still missing the input parameters for each function.

Ok, it would be best if you took a deep breath now; only one life left. Back to the Savepoint and start over.

ok let’s check if you are safe now:
✓ little functions with single responsibilities = check
✓ proper naming = check
✓ receives input and returns an output = check
✓ no side effects aka no state mutations = check
Let’s add one additional bullet point here:
✓ referential transparency.
This means that the function always gives the same output for the same input. This is also the case now with your refactored code.
Now you have created something called a “pure function”. You received your first component for your weapon: a pure function. You might complain now that the code will not work the way we refactored it because the separate functions will never get called. We will take care of this at the next level.

Stage 2: The Ghost of Composition
You have refactored the first parts of your code to some friendly pure functions. Let’s retake a look at what you’ve got so far.

You remember that it is all about putting these baby functions together. Alias put the output of one function to the input of the next one. Alias compose them. You think about how you can possibly do this with the functions in your inventory. And you come up with this:

But wait, you quickly determine that these const assignments are useless as your fancy wizardry hat, and you can rewrite it in one line:


Oh boy… that’s still really ugly, you think. And while you stare at your mess, you get blinded by a bright light. A slimy gibbering ghost appears out of nowhere, and he starts to talk to you with the voice of Kermit, the frog.
He says My friend: I. Am. The. Ghost. Of. COMPOSITION! I will give you the most powerful spell you will ever see — the ultimate spell of REDUCE. My friend: always remember, it will reduce two values to one. But friend, don’t use this spell reluctantly; instead of spam with it as much as you can, he says.
You say: Alrighty slimy ghost, but could you please stop talking in such an annoying mysterious way to me and also please stop slobbering on my screen? Just explain to me what I can freaking do with it!
But the ghost vanished without any answer.
Ok, I hate you, stupid ghost, but I will find it out on my own, you think. You cast the spell, and some weird example Reduce function appears in your code.

What the heck! We give this Reduce method two arguments. The first is a weird callback function. The second is simple. It’s just an initial value. In this case, it is an Integer of 10.
Ok, let’s go through the more complex callback step by step.
- Reduce looks first at what the initial value is. It is 10 in this case.
- Now it sets the accumulator to the initial value. So the accumulator is 10 now.
- Now it takes a look at the first value of the
sweetArray
.It is 1 in this case. - Now it “merges” alias “reduces” the value of the
accumulator
(10) and the current array value (1) to one single value. To do this merging, it uses the rules you have determined in the function body of the callback. - In this example, it uses simple addition to reduce the
accumulator
(10) andcurrentValue
(1) - Now you get 11. This will become the new accumulator value.
- Reduce will jump to the next array value. 2 in this case.
- It will continue with reducing/merging
accumulator
(11) withcurrentValue
(2) - The new
accumulator
will become 13 and Reduce will jump to the next current value. - … this goes on until we reach the end of the array.
So as the annoying ghost said, the most important thing to remember is that: Reduce merges two values to one by using the rules you have defined.
Oh, that’s pretty nice, you think, and you swing your mechanical keyboard again. Another example of a Reduce function appears.

Here you do exactly the same but with a completely different purpose.
- You set the initial value to an empty array []
- The acc becomes this empty array []
- Then we merge
acc
([]) with the first value of the array. Which is the first nested array [3, 2, 2, 4] - For the merge, the Reducer uses the rules we have defined. In this case, we do a concat.
- we concat empty array [] with [3, 2, 2, 4], so
acc
is [3, 2, 2, 4] now - Reduce looks up the following value ([4, 5,]) and concatenate it again with
acc
= [3, 2, 2, 4, 4, 5,] - Finally, we have flattened the
nestedArray
with the help of Reduce
Hmm, ok, that’s cool and powerful, you think. Indeed, it is so powerful that you can implement a ton of different functionalities with this one method. You can even replace other crucial Functional Programming functions like “Map” and “Filter” with “Reduce”.
Now you think: I should try what happens if I use the reducing magic on my ugly Composition. Jesus, your Composition immediately changes to a beautiful, highly readable structure.

And there also appears an additional Reduce function in your code.

Let’s check again what is going on here.
- the first parameter of pipe will receive all the functions (
reduceHealth..., checkFor..., reduceHealth...
) - pipe returns not a value but a new function
- this nested function will be also called immediately by the second brackets at the end of pipe:
pipe(...)(100)
- The nested function also expects a parameter
initial
. We assign the value 100 to it. - so also the initial value of the Reduce will receive the value of 100
Now the Reduce magic starts again:
- Reduce will run for every function we provided (like with the array value in the previous example)
- The initial value is 100 so
acc
is set to 100 immediately - now Reduce looks up the first provided value which is
reduceHealthByTwenty
. This iscurr
now. - now it merges curr with acc by the given rules. In this case, it does
curr(acc)
. Or to be more explicit it calls:reduceHealthByTwenty(100)
- This function call will return a new value (80)
- This new value will become the new acc and Reduce will proceed with the next function which we provided (
checkForDoubleUp
)
Isn’t this freakin AWESOME? You now stick the return value of one function into the input of the following function. And look: your final Composition is so amazing self-explanatory now. You don’t need to check any of the functions to know what is happening here. One quick glimpse at the pipe call is enough to understand what the code does exactly. It takes an initial value of 100 and then reduces it by twenty, then it checks if there will be a double up and at last reduce the outcome by half. Here is your outcome again, that you don't need to scroll up.

Wow, you think. That is cool. What luck that I am now equipped with this powerful Reduce method and this sweet pipe function.

Stage 3: The Insurmountable Obstacle
Ok, ok, ok, let’s do a recap: You have your little pure functions, and you also know how to compose them together with the help of a Reduce function, that you used to build the pipe. Haha, Functional Programming is eaaaasyyy.
You begin to refactor your code while writing pipe after pipe with a smile on your face. Man, killing demon functions is so satisfying. But then you trip over this function:

What’s this?? You get the same facial expression as your colleague from the sales department do whenever you explain something about your work. This freaking demonStealsPlayerFruits
function needs two arguments. But the previous function in our pipe spits out only a single return value. The provided output doesn’t match the required input. The function SHAPES doesn’t fit. You can’t pipe them. You will produce ‘undefined’ in some cases. Your last life is gone. Game over. Sorry man.

You begin realizing that functions can have different shapes. A shape is defined as what it expects as input and what it provides as output. Some functions expect two inputs. Some functions return functions instead of values; some expect arrays or other data types, and so on…Damn you funczy!#nal progrzmm!ng game. Ok, whatever, let’s google some cheats and the walkthrough to make this game solvable.
The Walkthrough

Walkthrough Secret Tip 1: There exist a lot of different function shapes. The major ones are the following:
- Unary function. It takes one input and returns one output.
- Nary function. It takes multiple inputs and returns one output.
- Supplier. It takes no input but returns one output.
- Consumer. It takes an input but returns no output.
- Predicate. It takes inputs and returns a boolean.
Walkthrough Secret Tip 2: Try to build Unary functions only.
Walkthrough Secret Tip 3: Avoid Consumers at all costs.
Walkthrough Secret Tip 4: If your function shape doesn’t match, you can use two strategies:
- Edit the function so that it matches.
- Build an adapter function that you can use to connect the two not-matching functions.
The Cheat Sheet
- Closure — Gives you a mutable state if you really need one in a safe way.
- Curry — will convert N-ary to a Unary function.
Stage 3 Again: The Easy Obstacle
With the help of the walkthrough and the cheats, you can now crush this stage.
Walkthrough part 1: function editing
Here is the function again:

You have a function that expects two inputs. You always prefer unary functions. So the curry cheat will work great here.
When you use the cheat, the following happens to the functions parameters:

The parameters get split up in their own and separate nested functions.
demonStealsPlayerFruits
is now returning a new function instead of a value. This new function expects the second parameter (inventory). demonStealsPlayerFruits
itself expects now only the argument for demonIsHungry
. This means that you can call now demonStealsPlayerFruits
with only one argument.

And no errors will happen. If you do this, you will return another function containing the remaining parameter.
Now you can provide the argument for the second parameter by calling hungryDemon(['sword',...])

You also can still use demonStealPlayerFruits
normally and provide it all arguments at once with the double bracket syntax:

Chances are high that you think now: ok Bro, blablabla.. but how can I use that hack to solve my Composition/pipe problem?
Easy: Fix one of the values by calling the first part of the curried function. Now you can use the returned function, instead of the original one, in your pipe.

Nice, you just used Walkthrough Secret Tip 4 and the curry cheat, and you edited the function so that it fits in your pipe.
Walkthrough part 2: function adapters
A little later in your code, you see this function. It is already curried, so it already can be used in a pipe. But what to do if, for example, the order of the parameters is wrong?

Let’s assume that you need inventory as the second curried parameter here, that you can use it in your pipe. And second, you can’t just rewrite the function because other parts of your code already use the same function. Your blood pressure rises, and you yell at your screen: I hate you Functional Programming — why do you do this to me. But remember, you have a walkthrough sheet. So take a quick look back to see what you can do about this.
Secret Tip 4: if your function shapes don’t match, you can use two strategies.
- Edit the function so that it matches.
- Build an adapter function that you can use to connect the two not-matching functions.
Quick recap: You used strategy ‘A’ when you split the functions params with the curry cheat. Now strategy ‘B’ the adapter function will come to the rescue. So go ahead and build up an adapter function. In this case, you need a flip function that brings the curried params into the proper order.

Now you simply use playerAttacksDemonFlipped
in your pipe instead of the original playerAttacksDemon function
Final Stage: The Mutation zone
Greetings, a weird drunken grumpy dude says to you. Why are you here? He stays in front of the entry to the mutation zone of your code — the zone where you have done a lot of ugly global mutations in the past.

You say: I need to kill all the parts where my functions manipulate or communicate with the outer world. Otherwise, my Functional Programming weapon will explode. So please step aside and let me in.
Hahaha, oh boy! This is a dangerous area. Before you are allowed to enter, he says, I will give you advice. And if you don’t listen, I will sell your soul to the marketing department.
And he begins with one of your most important JavaScript lessons ever. You need to understand one thing. You need to know how JavaScript works on the memory level. You need to know how to mix the magic potion of the EXECUTION CONTEXT and the Call Stack.
let’s say you have a function like this:

When JavaScript sees this function, the first thing it does is to save all global values to global memory which is part of the global execution context. For the previous example, it will look like this:

So let’s check step by step what’s going on here:
- JavaScript saves function
demonWatchesNetflix
to the global memory. Nothing gets executed yet. - Now it saves the label
demonInitialLaziness
with the value 10 to the global memory. - And in the last step, it declares a label with the name
newLaziness
.newLaziness
has no value yet. (neither is it undefined… this is because we can not reassign something with the const keyword. This is the reason why it is just declared ) - To resolve the value of
newLaziness
JavaScript needs to executedemonWatchesNetflix
. - As soon a new execution happens a new execution context will be set up
- This new execution context has its own local memory
- This new execution context will be placed on top of the call stack
Ok now let’s take a look at what will happen next.

- Inside the new execution context we will:
- First, save the label
laziness
to the local memory. It gets assigned the value of 10 in this case. - next, we will save the label
watchingHours
with the value of 5 to the local memory. - next, JS will run a conditional check
- the condition is true and the returned value of
demonWatchesNetflix
will get assigned to the (until now still unassigned) label newLaziness - the execution context is completed and gets popped from the call stack
Now, boy, you know to brew the magic potion of execution context. Now you are allowed to pass the doors into the dark mutation zone. But believe me, darling, you really shouldn’t go alone the first time. I will escort you. Mutations zone has the best liquor and cigarettes anyway.
Only one minute in the zone, and this mutant function crosses your path.

The drunken explains Every array method that mutates your original data structure can kill your whole code. Avoid them and better use array methods that create new arrays instead of mutating the original one. Map, Filter, and Reduce are the essential magic methods you will need. He kicks the forEach in his mutant ass until it switches to a Map.

A short time later, you both arrive at this function:

Your grumpy comrade says: It is accessing the global memory. We need to kill the global mutation before it spreads. He kicks it hard, and it switches to this function.

You understand that now there is no more global variable. But you don’t understand the change altogether. And then he says: Boy, let’s take a good sip of the magic potion of execution context and call stack. It will help you a ton to see what is going on in the new function.

- the
attack
function gets saved to the global memory causeDamage
gets declared but is not yet assigned.- To resolve the value of
causeDamage
, we need to executeattack
. A new execution context is built attack
gets added to the call stack- in the local memory of
attack
, ahitCounter
variable with value 0 gets saved - also, a function named
addOneHitpoint
gets saved to the local memory
Ok, let’s check what will happen next.

attack
returns its nested function with the nameaddOneHitpoint
- So the resolved value of
causeDamage
is thisaddOneHitpoint
function. Or with other words:causeDamage
is the same now asaddOneHitpoint
- the
attack
execution is finished, and the function gets popped from the call stack, and the execution context gets deleted
But we are not ready yet. There are invocations of causeDamage
left. And as we learned four lines earlier. A call of causeDamage
is the same as a call of addOneHitpoint
. So if we look at the JavaScript execution it will look like this:

- a new execution context for
causeDamage
is built up - we return nothing but we increase a variable
hitCounter
by 1
But what the…? There is no local and no global label named hitCounter
that you can increase here. Javascript has already deleted the old execution context. So what is going on here? Please help me, grumpy friend.
Hahaha, he says. Now, boy, you will learn how to use one of the essential cheats in JavaScript. The Closure.

Whenever you save an inner function from a local execution context to the global context, this function will look at what is around in the same local memory. And then it will pack all this stuff in its backpack and bring it to the global context. So in this example, we saved addOneHitpoint
to the global memory. When this happens, addOneHitpoint
will look around what else is in its local memory. And it will put all that stuff in its backpack and take it to the global memory.
So back to your problem with the not existing label. If JavaScript can’t find it in the local memory, it will look up if some of the functions in the global memory have the label in their backpack.

How can I access the value of hitCounter
directly? You ask the grumpy. But he laughs out loud and says: you can’t, stupid boy! You are only able to access the function which carries the backpack. And that’s the reason why this is such a powerful part of JavaScript magic. The value is protected by its function. You cannot mutate it from outside. The value is closed over by the function. This is the reason why the backpack is called "CLOSURE". And this is the concept you use if you really need mutable states with Functional Programming.
But be aware: even with states saved as protected Closures, you will violate the hardcore Functional Programming paradigm. That said, such a closed over state is beneficial and quite safe to use in practical JavaScript coding.

Bonus Stage: The Call Stack Inception
Now you are equipped with all the pieces you need to build the ultimate weapon of Functional Programming. The drunken says:
- You know that you need to build little baby functions that:
★ have a single responsibility;
★ ideally, receive one input and returns an output;
★ do not communicate with the outer world;
★ are referential transparent.
- You know how to build a Composition of these baby functions, which is nothing but reducing them.
- You know that you can edit the function shape of the baby functions with curry when they expect more than one input argument.
- You know that you can always build adapter functions that sit between two functions if the output and input don’t match. For example, if function 1 has a different output data structure, then function 2 expects it as input.
- And you know that you should save states as a closure if you really need a state.
There is nothing more to know if you want to start using functional principles in practical JavaScript programming. The grumpy kick some of your other functions while slowly walking back into the mutation zone to get his cigarettes and alcohol. But suddenly, he turns around and asks you: Do you want to level up your new build weapon right away to be even more powerful? Yes! You say. Sure thing. How can I do that?
Kill your iterations! He says.
Why that?? You might ask. For one simple reason: Look at the following loop:

With such a loop, you still are doing a state mutation of the counter variable. And state mutations, in general, should be prevented at all costs in Functional Programming because it can lead to strange side effects.
It is not so dramatic because the state is inside the scope of the function and not globally available. But anyway, it is way more functional if you get rid of such mutations and, therefore, the iteration itself. And you do that with recursion. This means that inside a function, you invoke the same function itself. So you call the function recursively. Ohhh, and by the way, you will love to work with recursion as soon as you are got used to it. At first, it will feel a bit strange and bulky to use it. But I promise that this will turn around completely as soon as you use it a few times.
Let me show you how this looks like for the previous iteration example:

Whuuuut??? To better understand the code, remember how the execution context and call stack works. If you follow the execution, then the following will happen:
- We call the function.
- We check the exit statement. You will find such an if statement in every recursive function call. It simply must exist. Otherwise, you will end in an endless loop of function calls. This if statement is also named “the base condition”.
- If the exit condition is false, we call the function itself again.
- This will generate a new execution context that will get placed on top of the call stack.
- You start over with step 1.
This means: as long as the exit condition is not true, you generate new execution contexts without deleting the old ones, and your call stack gets more and more stacked up.
Finally, the exit condition is true, and no more new function calls are added to the call stack. Now JavaScript will start to execute all the stuff on our call stack and pop it from top to bottom.

And yep, if you play this game too heavily, you will get a stack overflow error, which is nothing other than the browser stopping you from adding more and more calls to the call stack. It does this to prevent your program from eating up all the free memory.
Mmmkay.. but it is so weird to think about, so how should I ever come up with such a recursive alternative on my own? You say while wiping away the tears in your eyes.
Mimimi, don’t cry, you wimp, the grumpy says. Think about it this way:
First, define the base condition where you want to stop the recursion.
In this example, you want to check how many magic potions you have in your inventory. The base condition will be: If you have nothing in your inventory the number of your magic potions will be zero.

After that, think about what you need to put in as an argument when the next execution context is built. The argument needs you to bring one step closer to the base condition with the next recursive call.
In the example, you want to get closer and closer to an empty inventory array. To make this happen, you need to get rid of one item of the array with each recursive call.

And as the last step, you need to define what you want to do in each call. In this case, you want to check if the string starts with the phrase ‘magic_’. If so, you want to return +1 for every recursive call. It looks similar to a Reducer where we can do something like acc + 1 for every ‘round.’ And indeed this example could be even more easily solved by the mighty Reduce or a Filter function, of course.

And no, you don’t need to think about the execution context and the call stack every time if you build such a recursive function. Just set up your base condition and think about how you can reach it.

Wowwwwww! Congrats! You made it! You rock!
Of course, the stuff I showed you is just the tip of a giant iceberg. There are way more Functional Programming principles and even more fancy technical terms out there (like monads, functors, point-free style, etc.). Some stand for really cool additional concepts; others are just complicated sounding terms for the easy stuff. Unfortunately, that topic is way bigger than I can merge into a Medium story. But anyway: I showed you the most critical principles and, more important, I tried to give you a mental model for all that stuff, so the practical application of Functional Programming approaches should be more accessible for you now.
The End.
Hopefully, you had the same fun while reading as I had when I wrote the story. 😄 But way more important: I will take some drinks now with my grumpy drunken comrade and then feel free to join us kicking mutant function asses. ⚔️
Cheers Christoph
More content at plainenglish.io. Sign up for our free weekly newsletter here.