An intro to Functional Programming in JavaScript and React

This article is worth your retinal zoom-in time, but remember that good is subjective to the observer. This is about Functional Programming and data flow over time. This article is for people that haven’t looked that close at Functional Programming yet—or for those whom wish to build intuition about how to approach data flow through apps.

Bonus Note: Any patterns described here should be applicable in Vue and Angular and really any other programming language where the main focus is data or a user, while actions and events are involved. If something is going from somewhere to somewhere, we can start to sample ideas from Mathematics and Physics. We can create well-characterized patterns to solve well-characterized scenarios, and we can also discover interesting patterns.

The most important take away from this article is that immutability and maintenance of the integrity of your application’s state are among the most important, and those ideas will lead you to effective function composition.

Functions can act as labels… labels of function throughout your application. Sometimes people label too much and create a lot of granular functions. It is important to note that the inlet and outlet of each function represent a jump in dimension. Such abstraction can cause confusion when paired with granularity because you might be comparing two functions not directly connected.

I will show you an image of what it looks like to observe 3 dimensions at the same time:

For example, imagine some data started at the left-most dot and then traveled through the middle square towards the largest square. It’s like a flow of information over time. The dot is upstream of the middle square, and the largest square is downstream of the middle square.

The interesting part here is what happens at each dimension change. That’s the place where one function ends and another one starts:

You already know this inter-dimensional movement quite well. It’s the relationship between grandparent to parent to child to grandchild. The parent has control of the child because it is upstream. This is abstraction.

Abstraction can be confusing for the same reason comparing two sections of water-slide pipe can be confusing if you want to confirm that they came from the same waterslide. Imagine looking at two random pieces of blue pipe that look the same. Where did they come from? Hopefully you knew, or hopefully you can see ID numbers on them. The interesting thing is that this ambiguity can spawn utility — sections can be composed. If you had a function that accepts a name and makes it font-weight: bold for you, you can use that function anywhere, anytime you want to make something bold. You don’t have to care what’s in the previous or next dimension as long as your green dots starts with correct data and your orange dots end with the correct data. In that font bold example, as long as your function always gets a name, you’re good. As long as the font always comes out bold, you’re good.

If functions always move downstream over time, you can write out on a piece of paper the series of functions that kick off during an event, and you can quickly start to understand what you need to know to modify a system. While you are doing this, you are asking yourself, what data is involved, where is it coming from and going to, and why?
There will be smaller details, but you need to establish the branches of a tree before you can draw leaves, unless you assemble the tree asynchronously, but you will always start and finish drawing the tree.

Let’s bring it to a real example with two functions:

const drawBranch = () => {}
const drawLeaf = () => {}

Pretty simple, we have a function whose job is to draw branches and another one to draw leaves, one at a time it sounds like.

The parent in this case is drawBranch. It has permission to call drawLeaf. If you are inside the drawBranch function, you are in a dimension before drawLeaf. In this case, drawLeaf needs to know the location of the branch or it can’t draw anything. We must expect that the branch location will be included when drawLeaf starts. Both of these functions possibly don’t even care what else happens outside them, but it will always be critically important the order that they are executed.

It’s like a mixture of order of operations and a baton relay race. That place where the baton is passed is the outlet of a function and the inlet of another function. Things must flow in the correct order to work correctly. Keep this in mind when you are working with asynchronous functions.

A series of functions could be thought of as a component in some kind of hierarchy. Imagine the top of a pyramid is the root component. If you have multiple root components networked, you have microservices. A series of functions is conducive to composition. Imagine it like counting, 1,2,3,4,5,6,7,8,9,10. You don’t magically go back to 1 unless you add the word and on the end, or in JavaScript add the word return. The flow of numbers is one way through the chain. Note this imagery.

DATA FLOW

Functional Reactive Programming is all about thinking in data flows over time.

In Functional Reactive Programming, you are some data and you can go anywhere one hop at a time — from node to node — from a start to a finish. You can chain these hops, but it’s a special way of thinking. Something is generally always listening and then acting or doing and then triggering. Stuff can listen to events and trigger actions. This whole time, the state integrity must be maintained. Functional Reactive Programming emerges as a way to apply patterns to this kind of stuff. Changes are very formal so no one gets screwed up. Remember — order of operations.

Functional Reactive Programming is about pushing towards something or pulling something to you, one step at a time. Rather than one step, you could say one section at a time. Let me describe that slightly different. When you start something, you should finish it. When we draw that on paper, it looks like a fancy graph of actions and events over time. State isn’t mentioned there, but you can see it by looking at function inlets and outlets.

The point is, we are adopting a mathematical approach. Various patterns emerge when we study things over time.

Checkout this visual sampler pack of you starting and finishing something and experiencing things along the way:

Fig 1.2- Functional Reactive Programming (FRP)

I think this article will work better if we don’t load you up with JavaScript code examples. Let’s try instead to focus on how to think and why we look at certain types of code.

Directed Acyclic Graph

In the React world, you will eventually encounter this thing called a Directed Acyclic Graph. You barely know me, but I love studying nodes and relationships between them. I eat pieces of nodes for breakfast. My digestive system is a unidirectional flow over time.

Fig 1.3 — Directed Acyclic Graph (DAG)

When mathematically analyzing a React app, it should look like that DAG. Imagine the first node is where and when some data comes into the picture — maybe from your API. Imagine the last node is where and when it gets rendered on your screen. JSX is the language we speak at the last node. Get used to it.

The top of the DAG looks like this usually:

render(
<App />,
document.getElementById('root'),
)
Remember: we must first discuss a bunch of crap related to weather and climate before we start talking about the Aerodynamics of Wind Movement. Wind is the movement of air from an area of higher pressure to lower pressure. There is a pressure gradient force. The sharper the drop in pressure, the faster the wind measured. Data flows exactly like this — only way different. We have to wander into a bit of complexity before we can simplify it.

Flow

Data can flow both ways. It may first flow unidirectionally from server to client, and then it may flow back unidirectionally. It’s like a one way street beside a one way street. Some call it a road. Chaos occurs immediately if this flow rule is broken. You wouldn’t send data to the server through your GraphQL get query, or would you? No, you wouldn’t. Data flow is best served unidirectionally. It’s simpler to reason about.

You might be saying, “hey wait what about two-way binding?” It’s still of happening unidirectionally. The button press always leads to the button press event which always leads to the state update action which always leads to the UI reflecting the result of that update. State, events, and actions — there they are emerging again. They always do.

Data-centric apps are best served with this type of logic because data flows inside the network that contains it. Data is like a type of energy. It can cause a chemical reaction that cannot be undone. If someone posted a photo on Facebook of you wearing a cheetah-print pajamas while you were crying, you would understand how data can create a chemical reaction. Once you see it, you can’t unsee it. Data also moves like a fluid. It can create a huge mess if certain kinds of this liquid leak out.

The internet is a series of tubes, and so is your app.

PART ONE — THE IMPORTANT THEORIES

Patterns and Anti-patterns

Study of JavaScript and React brings mentions of anti-patterns into the mix. When a normal human sees things, they have normal knee-jerk reactions. An anti-pattern is a response to something that is a normal but sub-optimal response. Everything comes back to patterns, flow patterns.

Try not to make fun of someone that isn’t familiar with your patterns. They may be reasoning about the situation from a different angle, and this will change the outcome they observe. Just as I used the word this a second ago, the word this can change depending how you analyze some data. Let someone know your pattern. Draw it for them. See if they still disagree. But, talk about it like Bill Nye to Bob Ross composed with Mr. Rogers.

Fig 2.0 — Bob Nyegers: he paints nice logic in your neighbourhood.

You may or may not notice immediate benefit from these flow patterns, but you will benefit from knowing they exist. After all, JavaScript was once a savage and choatic environment, perhaps like Mars is or Earth once was. Data was gained, keyboards were smashed like WWE Wrestling, but the JavaScript beast was tamed — at least more than it was anyway. LOL. That is a cop-out statement.

In JavaScript, things flow the best over time immutably and unidirectionally. Why? Because of live references and dynamic typing.
  • Objects contain data.
  • Functions instigate movement of data.

Functions can create action, like pushing a ball with just enough force to roll down the stairs and completely destroy my sister’s Mousetrap game that was setup.

Fig 2.1 — Mousetrap Board Game
  • Notice how pushing a ball down the stairs is a one-way action as part of an event.
  • Notice how Mousetrap is a series of contraptions and a one-way system. If you ignore every point in time individually at first, you can study the system as if it were in continuous motion (ie: Calculus and deterministic transformation formulas).
  • Notice how each contraption always produces the same result unless you get violent or introduce an unexpected flow over time, like turbulence. Try putting a large fan beside the game and see what happens when you mutate the air pressure values around each contraption.
Note: forget about deterministic transformations for now. Your brain may catch on fire. I only mention it for the guy with the beard and his coffee. Let’s also note the gender-specific reference here and always be mindful to include females and non-binary individuals.
Deterministic means, “if you put in the same input, you get the same output 100% of the time.”

If I always roll a ball down the stairs, and it always destroys my sister’s Mousetrap game, my mom may not appreciate that pattern, but at least I can tell her it is deterministic.

I refuse to go anywhere else before I supply you with definitions for live references, immutability, and garbage collection. These next things make the most sense if you ask yourself how they are related to unidirectional travel over time.

Live References

In JavaScript, Functions are first-class citizens. What does that even mean? It means that in JavaScript, everything is an Object. Functions are Objects too, so that’s at least confusing, but Functions are a first-class citizen because all JavaScript does is start executing the first line of code and then stops when it’s done. Remember the order of operations? JavaScript just executes functions as fast as it can one at a time.

Slow down: It’s understandable if that doesn’t fully make sense. To understand more, you will need to research the JavaScript event loop which has a call stack, function queue, and the heap. The call stack is single threaded, so you can only process one function at a time, but the good news is you can load up an infinite number of functions in the queue and they will complete as fast as they can according to laws of Mathematics and order of operations.

The JavaScript engine is basically executing functions by passing them through the call stack one at a time (this is where the word synchronous comes from), but JavaScript also has a function queue, which is where functions that aren’t quite ready yet will be placed. We call this area the event loop, and you should research it — and not just once. You should read about the event loop once per month for the next two years. I’m serious. It’s a good idea to try to fundamentally master the event loop idea. Your ability to solve extremely crazy asynchronous bugs depends on it.

Think of it this way. When people buy stuff, they use money — units of currency. In JavaScript, the unit of currency is Functions. They may contain things, but you can imagine functions floating around. Everything happens inside functions. They are containers for work. They have a discrete start and a discrete finish (hint: unidirectional).

Fig 2.2 — Pass-by-reference vs. pass-by-value

When people say live references, they are talking about pass-by-reference. Objects and Functions maintain a live reference to a single source of truth. It’s like your home address, except the data lives inside the computer’s memory (or another location if you want to get crazy). The point is it’s somewhere exact, and it’s only that place your app refers to.

Here is an example of creating an Object in JavaScript:

const sandwich = {
flavour: { confirmed: 'pretty good' },
isTasty: getFlavour(sandwich.flavour),
}

You can see here the Sandwich Object has properties such as flavour, but it can also look at itself in real-time if you give it a function to do so. Well that’s kind of weird, but we like it. In that example above, the Object maintains a reference to getFlavour(). This is fundamental to immutable JavaScript. It is a common task to break references and throw them in the garbage. Part of your job as a JavaScript programmer is to manage these references effectively.

Fig 2.3 — Look at this as if it were a single object that doubles as a function too
Where this becomes dangerous is real-time pass-by-reference analysis.

Remember how the start of a function and the end of the function is the most important time and place. If the end of a function is relying on a live reference, the reference must be correct — or you’re screwed. It’s perfectly valid to take special precautions in your own code to ensure this data is always correct. In my books, you are safe to get excited about trying to protect your data.

What do you think would happen if you started producing hella sandwiches with your function and someone changed what was inside getFlavour()? We might start to see some pattern violations. You must be able to rely on the same output every time you give the same input. This is our first sampler pack for live references.

Advanced Note: This is also rule number 1337 for function composition. You can’t create a series of events and “set your clock to it” if you change the third step to modify the clock’s time by a random amount 50% of the time. That sounds weird and it produces unreliable results. It’s not mathematical, and you don’t want it in your app… unless you’re working on TrollCalculator.exe.

If someone changes sandwich.flavour right before you eat it, you may experience something undesirable if you measured sandwich.isTasty immediately after. Case in point. The cure for this potentially-horrendous flavour at the time of measurement is called immutability.

Immutability

Immutability is great because you can relax once you set data in place. You don’t have to worry about data or sandwich flavours getting unexpectedly mangled if someone else happens to observe that single source of truth around the same time.

In JavaScript with immutability, an Object is like a piece of paper with some writing on it, and you can fold it up and throw it like an airplane. If it was built with correct patterns, it could travel as far as possible as efficiently as possible. That’s what survival of the fittest looks like in programming. It’s what maximum efficiency composed with maximum effectiveness looks like.

If the airplane mutates along its journey, that’s not for you to decide when you throw it. Your job then is to throw it with the correct parameters. This is so important that you will see hand waving and emphasis in real life.

Your job as a programmer around this time is to imagine, ‘what would happen if everything about my app changed before or after this moment?’ It’s hard to predict the future and hard to control the past, so you should try your best to make the past and future not important for current. This means you should try to supply what will be needed now, and not worry about what will be needed later.

Immutability can free you from trying to predict the future and recount the past. Who cares where we came from or how we got here. We care where we are going next and how we are going to get there.

If you want some mental imagery, imagine traveling lean vs. hoarding. Remember, we are traveling unidirectionally. We have to supply everything without being wasteful. Data waste translates into extra CPU, memory, and bandwidth usage. It can also create leaky abstractions by leaking out too much information. Be careful out there.

This whole supplying too much or too little thing is fairly key to function composition and creation of chains of functions. If you make a long chain that goes deep under ground, it’s not efficient to go back and grab something you forgot. You don’t have to predict the future as much as it sounds like though. There is something very special we can do. We can pass objects through the function chain.

Check out this next example. Let’s pretend we are going to create a function that paints a news article onto your screen using JSX. If you’ve never seen this pattern, pay close attention:

const config = {
title: 'Sandwiches can produce good flavour',
author: 'Alice Bobson',
timestamp: 128364096092,
}
const renderArticle (config) => {
return (
<div>
<h1>{config.title}</h1>
<h3>{config.author}</h3>
<span>Created: {Date(config.timestamp)}</span>
</div>
)
}
renderArticle(config)

The thing I want you to pay attention to is the fact config is one Object, and it is getting passed into the renderArticle() Function. The function expects to be supplied with what it needs. We could create a unit test to ensure config always goes in. We could add logic to the function to make it explode if title, author, or timestamp are not Strings or are missing. Notice how the code example looks fairly neat as well. It’s quick and easy to understand.

Tommorow, we might remove the timestamp because someone complained it was too ugly. That’s fine, just remove two lines of code. Do you notice anything else? Yes, we forgot to put content in there. That is a pretty terrible article. Let’s fix it:

const config = {
title: 'Sandwiches can produce good flavour',
author: 'Alice Bobson',
timestamp: 128364096092,
content: 'Today, we added one key to an object, and we found that
our code was easy to test, extend, and maintain.',
}
const renderArticle (config) => {
return (
<div>
<h1>{config.title}</h1>
<h3>{config.author}</h3>
<span>Created: {Date(config.timestamp)}</span>
<div>{config.content}</div>
</div>
)
}
renderArticle(config)

What this means is the code is mostly hands-off once it’s working. You won’t have to restructure it very much if you add or remove anything. It’s a demonstration of both object composition and function composition. It’s a demonstration of why we usually like declarative code.

Let’s reflect on these key factoids:

  • Our function expected to be supplied everything it needed
  • We supplied our function with everything it needed
  • We stored everything in a config Object
  • The function performed an action, deterministically
  • Our pattern is very robust

In our example above, the logic is unidirectional. See how the data is flowing one-way over time? Data flows in at the top and then flows out. It will render the same article every time you pass the same values for title, author, timestamp, and content. You can trust the heck out of that render function if you can trust your config values are correct when the function starts.

Back to immutability — storing a piece of paper with a word on it at the corner of your desk can be done immutably if we use a pen instead of a pencil. If we want a different word on the paper, we can just put a different piece of paper there with the new word. It’s no big deal. In JavaScript, it costs like 2 cents to perform this switch:

  1. copy the Object to a new one,
  2. stop referring to the old Object,
  3. start using the new Object from that point on.

Here’s a real example:

const user = {
firstName: 'Alice',
lastName: 'Bobson',
}
// We just learned Alice's age. We need to add it.
const userWithAge = {
...user,
age: 1337,
}
return userWithAge
Note: if you need to research the spread operator, then go ahead and load up another browser tab for that. That’s the ... symbol above.

We could have done user.age = 1337, but that is not thinking immutably. Since we will never mutate user after it is created, we know it will have the correct data in it when we go to copy it. This whole immutability thing becomes super important in asynchronous code.

We might have multiple areas of an application reading user at the same time. Sometimes 0.15 milliseconds is enough to ruin your life. It’s not enough to feel comfortable just because we know we can do user.age = 1337 today. What happens if someone modifies another part of your application tomorrow and isn’t aware of that mutation? Immutability is a protective force and a protective layer.

Now you are understanding. I know you hear me.

Immutable, unidirectional data flows can lead to simpler apps. If data always travels one way, you can look for the exact moment in time something went wrong, and look upstream leading up to that moment in time. You will also want to look at what other functions are trying to do at that moment in time. This is true especially when it comes to mutating data or this.setState() in React. I wrote another article about setState that you may enjoy reading:

Unidirectional data flow is easier to reason about because it’s simpler. It’s simpler because you are generally caring the most about what data or state is right now, not before or after. There is a distinction however between simpler to reason about code and simpler code:

const fn = (x = {}) => ({ x })
Remember, the shortest, most concise possible code isn’t always the simplest to understand.

Just because something is more concise or more simple looking does not mean it is simpler to reason about or to quickly understand. Have you ever seen code that someone told you is the fastest possible way? Does it look like cryptic garbage that makes your brain hurt immediately? It’s like sending a zip file into someone’s brain and forcing them to unpack it before they can understand.

In the case of Redux, some people find it’s too much boilerplate. This means they don’t like the verbose way it makes them accomplish a task. In Redux, Data and actions flow forward in time, and data and changes flow forward in one direction. When you are comparing things over time, you can rely on first-on first-out (FIFO) logic.

Redux embodies unidirectional flow and immutability. Try not to hate Redux until you understand its patterns. What Redux gives the world is a formal state-change management process that is easy to reason about. Anyway, let’s move on.

Think of immutability like the Ronco Food Dehydrator:

Fig 2.4 — Ronco Food Dehydrator

Immutability works best when you let data flow unidirectionally. This way, asynchronous code can appear synchronous because everything is “over time” (ie: from the start of the function to the end of the function). This is the pinnacle of declarative code, and it makes sense because time moves one way. It’s easier for your brain to handle.

Think of an action or an event as a function that gets triggered at a particular moment in time. When is always significant. If a bug happened, something happened at the wrong time and mangled your precious deterministic flow pattern. Similarly, something may have not happened at all at the perfect time.

This leads me to my next point. After the time passes when some data or functions are no longer needed, they become garbage. Who better to come and clean up those live references than the garbage collector. What does this magic “GC” do?

Garbage Collection

To understand this deeply, we can re-visit our immutable pen and paper example. If you replaced the old paper with the new paper but you never got rid of the old paper, your home would quickly look like this photo:

Fig 2.5 — Depiction of a JavaScript application without Garbage Collection

To combat this problem in real life, a garbage truck periodically drives by every two weeks. The driver fills his nostrils with magical garbage scent as he takes that landfill nectar away from your home.

In JavaScript, it is slightly different. JavaScript garbage collection happens when nothing in the current execution context refers to an Object any more. Everything is more complex when you zoom in, so let’s keep it that simple.

In JavaScript, the execution context is usually a function being executed.
const garbage = () => {
// When you stop referring to me, I will be gone
return 'cool'
}
const generateGarbage = () => {
const createReference = garbage()
  if (createReference === 'cool') {
// Perform some work
}
  // As soon as we arrive here during code execution,
// JavaScript unloads `garbage()` from memory.
return 'that is very cool'
}

In the example above, JavaScript puts garbage() in the memory as soon as it encounters it. That occurs at the first line inside generateGarbage(). JavaScript must hang on to that reference in memory because it knows you will refer to it again soon and because loading things into memory costs CPU time. JavaScript is super fast because of this, but you can get into trouble.

General Rule Note: As soon as you know you no longer need a reference, ask yourself, is this going to get garbage collected now? Your goal is to notice when the answer is no. This is how you avoid memory leaks. It is a key JavaScript skill.

Garbage collection is like a robot helper following your functions around and auto-dumping your no-longer-needed Objects and Functions into the trash shoot of an apartment building.

I don’t want you to freak out because this stuff isn’t exactly easy. If you aren’t very familiar with it, it’s normal. Spend a bit of time studying them. Here’s a good article to give you some things to think about: https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/

Garbage Note: Garbage Collection is cheap. It’s so cheap, we don’t really care about its cost until our JavaScript problems become more serious than “what is functional programming?”. We don’t care too much about premature optimization. StackOverflow.com will help as soon as you describe a Garbage Collection-related problem.

Understanding this stuff intuitively may not happen on the first pass. Based on my observations, some people do not love JavaScript, React, or JSX. If they are not familiar enough with JSX, we cannot be mad at them for being skeptical towards the unknown, but we can help them understand more. New insights can flow towards you from new data. Send haters this article, and they will at least leave you alone for a while since it takes a while to read, LOL.

At some point, I would appreciate two things very much:

  1. The dopamine release from seeing +1 clap on my Article. Don’t even get me started on +50 claps. That is some crazy stuff to see while you’re observing the accumulation of total claps over time.
  2. The dopamine release of seeing +1 comment on my Article that offers negative feedback or constructive feedback.

None of my articles will make sense if we aren’t approaching software as a set of smaller problems and solutions that are composed together to create one solution. Kind of like a Singleton. If you look at it, it displays the correct data at that moment in time when you looked. Sounds like data is important right? Data flows through a series of tubes, channels, or streams. Tune in to channel 5 to see how many users are logged in currently.

I’ve hidden a secret puzzle in the bolded words above.

Now, we are about to push into some more advanced territory. Imagine it this way, we are coming into this new material here, and I’ve supplied you with everything I think you need to properly reason about this.

We studied unidirectional flow, live references, immutability, and garbage collection. We are now going to start composing those more basic ideas into something that will blow your socks off.

Function Composition

This is the meat and potatoes of functional programming, and it’s a secret layer behind reactive programming. After all, you can’t use map, reduce, and filter if you can’t chain them onto the end of a collection of data (ie: an Array) that’s flowing somewhere.

You can compose not only functions but also entire data flows together as long as everything together flows one way and is deterministic. There is a thing called determinism. With respect to functions, a deterministic function produces the same predictable output when you put in the same input. These are pure functions and they are worth studying at great length in your spare time.

A deterministic function produces the same output when it is given the same input.

In programming, there is a thing called referential transparency. It means, if you swap out a function for its computed counterpart, the application would still function identically. You can’t have referential transparency without a deterministic function.

Here’s an example because referential transparency makes everything easier to understand:

const ok = 33
const getNumber = (num) => num // returns 33 if you input 33
const correctAnswer = 1 + getNumber(ok) + 7
console.log(correctAnswer) // returns 41

In the example above, if you swapped out getNumber(ok) with 33, correctAnswer would still return 41. That is referential transparency, and it will be an incredible boon to your ability to create bug-free, easy to change code.

Take a look and imagine some data flowing through here:

Fig 3.0 — Function composition

Check out that radical cube there. What do you think would happen if g() randomly output something unexpected 50% of the time? That would mean f() would receive something unexpected 50% of the time, and everyone would have a bad time… 50% of the time all the time. Also, good luck putting both of those functions into another function that is feeding the result of g() into f().

Brain Helper Note: g feeds into f and both are deterministic.

If this was in JavaScript, it would look like this:

f(g())

If you ever studied Calculus, you may be thinking to your self, holy expletive, this looks and sounds familiar. If you did not study Calculus, then imagine people drawing triangles inside semi-circles and realizing they can estimate things infinitely close to exactly correct — things such as distance and energy use over time if they were supplied enough information. Does that remind you of composing some functions together with supplied data? What if we switched the word function for equation?

What if we used equational reasoning and said that function = equation?

If a function is an equation, then we could always try to compute something and update state or return something.

You could think of state updating like a:

  • push
  • put
  • set
  • do
  • write

You could think of returning something like:

  • pull
  • get
  • bring
  • collect
  • read

Return is a pivot point. If you look at the answer to ‘what is returned?’, you see where the function landed and what the state will be.

Let’s see a JavaScript example. Let’s imagine we are data and we are going to the other side of this function:

const locationX = 'http://www.com/profile.html'
const config = {
name: getProfile('Reader McGee').name,
start: '1337.html',
end: next(locationX),
error: '404.html',
}
// Destructuring not used in order to show correlations
const arriveAtLocation = (config) => {
if (config.start !== '1337.html') {
return config.error
}
  return config.end()
}
arriveAtLocation(config)

If you come across this code above, you could ask yourself:

  1. Where is it going?
  2. What is being returned?

We could burst in with a bit of equational reasoning again and say:

Where it is going = What is returned

The function takes one hop and computes where it is going.

In Calculus, it probably looks like a baseball being thrown. It probably has a vertex and a place in time when the tangent is flat. I have to include this reference because some people will know what I’m talking about and they will see imagery.

As you can see, the function expects to be supplied: config.start, config.error, and config.end.

All that matters to the function is it’s getting supplied the correct data and it’s going somewhere and/or returning something. If it doesn’t reach outside itself for any reason, then there are no side effects, and it is deterministic — and unidirectional as well.

You could make a unit test that checks that. Such a unit test could check that the correct output was returned for a given input, then we would know everything was working as intended.

Upon my further inspection, the previous example may be a bit difficult to understand the first time you encounter it. I will help us by pointing out a couple facts:

  1. The function is being supplied what it needs
  2. The data being supplied is dynamic, but the operations in the function are static
  3. The data types are static. We are relying on this fact.
  4. The function returns something

I think a good way to look at chaining functions together is to consider that you are wiring up a scaffolding or a series of channels, and then you are energizing them with data after. A chain of functions is like a structure. The intent is to wire up everything so that you can freely change little pieces here and there without completely refactoring everything. This results in time-savings later because you never know what might have to be changed.

When functions always return something, functions can become answers to questions. For example,

const users = getNumberOfUsers()

When functions always return something, they always finish with a final step similar to handing in your homework at school. Your app can check the final outcome and make sure everything is normal.

The important thing to keep in mind is that data is flowing unidirectional over time. The data is flowing from start to finish (and from parent to child), perhaps from your API towards being rendered on your screen. Look at Exhibit A again. Maybe g() is getTheBlueIcons() and f() is setTheBlueIcons(). Now, look at Exhibit B again. Maybe f•g() is renderIcons('blue'). As long as you got blue under control, you can sprinkle a bit of win sauce on your app.

What we are actually doing here is thinking declaratively. We are declaring what the start condition and the finish condition look like. If something goes wrong and you know your start condition is correct, then you know where the problem must be. We are thinking “with respect to time”.

Do you know what Exhibit B really is? It’s a pattern. It’s function composition. Some awesome people noticed this a long time ago, such as Isaac Newton and Gottfried Wilhelm Leibniz.

Fig 3.1 — Gottfried Wilhelm Leibniz
Quick Research: https://en.wikipedia.org/wiki/Gottfried_Wilhelm_Leibniz
My brain, Gottfried! WTF were you doing in the year 1700 AD?? Is this real life??

It used to take me two pages to answer one question in Calculus. Keep that in mind when you realize these guys were from 1700 AD.

Fig 3.2 — Luckily, I don’t need to do this in JavaScript

The order that things occur inside your application is very similar to BEDMAS or PEMDAS (order of operations) when doing Math. In the above image, the important thing is that everything happens in an exact order over time. Because the order is predictable and the functions are deterministic, the finish value is predictable if the start value is predictable.

The difference is you are doing Math with units of logic instead of numbers. Remember, a function is just a place to do work. The name of the function acts as a label. If you follow some rules, you can unlock mathematical precision.

Conclusion

Alright, now we are familiar with composing deterministic functions while immutably flowing one way.

Think of your functions like formulas or equations. If you put some stuff in, you will get what? Do some bonus research now into shared mutable state and side effects. Shared mutable state is the more complex answer to, ‘why do we care about immutability?’ Side effects are the more complex answer to, ‘why are deterministic functions good?’

I use the term deterministic instead of pure because it is more academic, so you will find scientific information very quickly when you use Google to ram your head into the keyboard.

I’m thinking about a more complex version of this article and how to execute these ideals in JavaScript. Until then, you could follow me on Twitter or Medium or a number of other places if you are interested. I typically randomly burst out information that is valuable towards JavaScript, React, React Native, QA, UX, and even Economics and Marketing.

If you simply cannot wait to learn more, I recommend reading Eric Elliott’s article about function and object composition. Now that you’ve read this article, you may find Eric’s words about object and function composition very timely.