An intro to Functional Programming in JavaScript~ (and React and/or Vue)

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: patterns described here should be applicable in React, Vue, Angular, and really any other programming language where the main focus is data or a user — while actions and events are involved. Imagine for a moment, 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. Abstraction can cause confusion when paired with granularity because you might be comparing two functions not directly connected together; you also lose some freedom of access to local data if you move upstream.

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

Fig 1.0 — Dimensions

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:

Fig 1.1 — Some kind of inter-dimensional movement

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.

Fig 1.3 — Waterslides with data as humans

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 inputs a name and outputs the name withfont-weight: bold added to it, you can use that function anywhere, anytime you want to make something bold. You see the math in there too right?

fn(name) = (name + bold)

It’s kind of important that we stop for a moment and look directly at that = symbol. It means, at the moment in time we observed both sides of the equation at once, they were equal to each-other with infinite accuracy and precision. Notice the word at in my statement. I am describing a co-ordinate in space-time. I don’t really need to care where you are while you’re observing the equation. I can tell you with 100% certainty that they are equal at your current location at the moment in time you just looked at the =.

If you were a function, I could derive all kinds of information about your whereabouts and how you got there, and possibly where you are going. Math and Physics comes in so hard here because there are data flow vectors in play. If you were a baseball thrown at a known speed and angle from a known place at a known time, I could predict where and when you would be at very specific places.

Fig 1.4 — The slope of the tangent is 0 at the vertex

Maybe the throwing action can be a function, and you can calculate what to do with the baseball next at the moment in time it hits the ground, or someone in the back of the head. You might do something different depending which option it ends up being. You could split that throwing action into two functions: one that takes the baseball to the vertex, and one that takes the baseball to its final destination based on the amount of energy you supplied to the throwing process. 50% of the potential energy was used in each function. The point is there is an inter-dimensional-shift between each function, and that they are connected together. The outlet of the first function was equal = to the inlet of the second function at the moment in time they connected together. The flow of baseball is unidirectional.

In the image above the waterslides, you don’t have to care what’s in the previous or next dimension (waterslide pipe-section) as long as your green dots start with correct data and your orange dots end with the correct data. That’s what makes the = symbol true. 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.

const isBold = (name.bold === true);

The above sampler pack there is a predicate meaning it only returns true or false, so it allows you to make a definition that works in any context to give you a yes/no answer about something at a particular moment in time. Predicates, and even predicate functions (a function that returns true or false), are extremely useful. We should always look to them first when our goal is to trap something down to a black or white response.

Predicates: we don’t want to care when it actually is true or false, we just want to set it up so that it will definitely change at the correct moment in time when some specific criterion(s) are true. This is a low-key form of decoupling things that matter from things that don’t matter.

Imagine you’re some data flying through a function. You need to make a decision:

const phoneNumberOfDavid = '555-123-4576';if (isBold) await call(phoneNumberOfDavid);

Ok, now that’s done unless call() explodes. If David changed his phone number, that call function would still work, so who cares what it does as long as it works with your supplied data (radical hint: functions are labels). Notice how the code is written with respect to time as you are reading it, and that sounds Mathematicsy. Notice how there is a secret = inside that isBold variable, and a moment in time just came when you observed it.

If functions always move downstream over time, you can write out on a piece of paper the series of functions (actions maybe) that kick off during an event, and you can quickly start to understand what you need to know to modify the series of functions. You can ask yourself: what data is involved, where is it coming from, where is it 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 some functions:

import FibonacciAdapter from 'some-outlet-right-now';const makeLeafOnBranch = (data) => {
const scalingFactor = 1.618;
return FibbonacciAdapter({
action: 'ADD-LEAF',
someRatio: scalingFactor,
someData: data,
});
};
const drawLeaf = data => makeLeafOnBranch(data);const drawBranch = drawLeaf => drawLeaf();drawBranch(data => drawLeaf(data));

Pretty simple — if not a bit savage for something to just suddenly look at — we have a function whose job is to draw branches and another one to draw leaves, one at a time it sounds like. Notice how I labeled everything exactly what it’s doing using as little words as possible. I don’t say, “hey what’s (1+1)+2?” if I can say, “hey what’s 2+2?”.

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 clever reader might think of premature optimizations when they transform 1+1 into 2. Is that actually important? On one hand, it’s harder to package 1+1 into 2. On the other hand, it’s harder to unpack 2 into 1+1. what if you need to access 1 but it isn’t available at a particular moment in time? Think about this example for a couple minutes and imagine things being unfortunately tucked away too far, or too much crap flying around that could be trimmed down. It’s easy with 1+1, but what if instead it was 2+1+2 and a moment in time came when you needed to know how many numbers were used to make 5.

Keep this in mind: the penalty of the wrong abstraction is worse than the penalty of no abstraction.

The above quote that I observed once at some moment in time before now is telling us that it’s easier to untangle one function than it is to untangle 45 functions that do pieces of what the original function did. It’s the same but different. 1+1 leads to 2, or 2 leads to 1+1. Your task is to figure out the most concise code that conveys the perfect amount of meaning to make it look like poetry and mathematics had a baby in some data flow with respect to time. The poetry is in the labels, and the math is in the movement with respect to time and the relationships between labels.

Here is an aggressive example, and yes it’s horrifically labeled, but it shows some unidirectional movement from A to D:

const d = a => (a + b + c);

I want to just write that again another way so you can more clearly see the synchronous, deterministic flow with respect to time:

function d(a) {
const startPlusCrap = a;
startPlusCrap += b; startPlusCrap += c; return startPlusCrap;
}

You can see how the starting input is dragged through there towards the startPlusCrap output hole. Hole is kind of a horrible name. It looks more like the end of a tunnel doesn’t it? It’s like a tunnel that goes into a hole of unknown, and that’s fine. Notice how my super-concise, syntax sugar version above uses ( and ). I did that intentionally because it’s similar to using brackets in math. They are part of BEDMAS (aka PEMDAS). The function is implicit-returning an expression, and those variables must appear a certain way for the = to work. We don’t want any confusion.

Back to naming things, naming things is like a creative science where you have to label things so another human with no idea WTF is going on can quickly see what’s going on. There’s maybe some lesson here about context at a moment in time when you observed some code running in its natural habitat. Interestingly, less is not more when it comes to labeling things, but a certain type of beauty emerges when you find maximally efficient naming patterns.

In my opinion, an Array named filteredData is generally not as good as one named filteredCountries or countriesFilteredByDate because it can make a huge difference depending where and how you see it being used inside other things. While you’re scanning code, you don’t want to be wondering things like “wtf is filtered data?”. filteredData reads more like a more verbose version of x. It’s not an exact science. Pay close attention when you have to start opening other files to see what something is. Well-chosen names can help protect against that confirmation step.

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 with respect to time.

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

Fig 1.4 — 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 (DAG)

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.5 — 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 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 way things appear. 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 — they can also code great if they choose to.

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 (ie: state: the state of the data at a moment in time when observed).

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.

Tomorrow, 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.

Notice that even the DOM markup inside that function has an order to it: first, you drop the <div> onto the page, then you drop the <h1> heading and put the title in it, then you drop the <h3> heading and put the author in it. It’s kind of like a 3D printer, printing the UI layer by layer.

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 moment 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 later:

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 quicker to 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, state, events, and actions flow only-forward in time (ie: they move only in the positive 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, at least in theory. We can always make a complex program that makes simple ideas inevitably complex. 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 perfect 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 the JavaScript heap 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 onto 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.

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 = 33const getNumber = num => num // returns 33 if you input 33const correctAnswer = 1 + getNumber(ok) + 7console.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 total distance, surface area, speed, and energy use over time or at exact moments in 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 an 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 traveling to the other side of this goToLocation function:

const locationX = 'http://www.com/profile.html'const config = {
name: getProfile('Reader McGee').name,
start: '1337.html',
end: () => next(locationX),
error: () => next('404.html'),
}
// destructuring not used in order to show correlations
const goToLocation = (config) => {
if (config.start !== '1337.html') {
return config.error()
}
return config.end()
}
goToLocation(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. As you can see, the function expects to be supplied: config.start, config.error, and config.end. The important thing here is that the data changes but the structure around it never changes. This is why it’s like an equation, except it’s more powerful because you can add logic like:

if (today === 'monday') use('multiplication')
if (today === 'friday') use('division')

All that matters to the function is it’s getting supplied the correct data and it’s going somewhere and/or returning something. If a function 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 later inspection, the goToLocation 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. A good function is one that can easily handle unknown types of changes later.

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.

More specifically, you don’t have to load up your short term memory with 900 small details of imperative code that “gets users from a database” if you can boil it down to a function called getUsers. Now every time you see your code, you see one word: getUsers. It’s a solved problem; no one cares what’s happening inside that function as long as it works correctly when it is called. This makes your code look really simple, so it stays relatively pretty easy to understand what’s happening. You can see the important details, concisely.

If the function was instead called getUsersAndAddTimestamps, then again, all the important information is captured in one word. No matter where you see it, you know what’s happening inside that function. You don’t have to look inside it.

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 so 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. According to other people, I tend to spray out gems.

I typically randomly burst out information that is valuable towards JavaScript, React, React Native, Vue, node.js, QA, UX, and even Economics and Marketing. I love revenue-generating assets and compound interest. My screen name is usually agm1984. I am in love with the equation ui = fn(state). As state changes, the UI reflects exactly those changes. The deltas are proportionate.

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.

In closing, like I said back at the beginning of this article, immutability is how you can sidestep many problems, and it’s the basis for unlocking the power of Math and the continuous motion of data flow, and if your state gets screwed up at any moment in time, chaos will emerge, so focus on guaranteeing state integrity. Your state changes should be very formal.

Functions can always operate within bounds, so there can be a lower bound and an upper bound. I’m catering to a bit of logic related to digital vs. analog values. If the value can be within -100% to +100%, anything outside that is out of bounds and your function should get mad at that moment in time. 0 is in the middle, so the distance between -100% to 0 and 0 to 100% is identical. Any function can always check its values before it completes.

Keep your types static. If it’s a String, it should always be a String, even if it’s empty. Use default values and default state. Focus on accumulating final products. Every function can start with some input to use as part of it’s default state, and then every function can also end with some output that was accumulated starting from the default state.

You don’t have to understand everything I described in this article, but you can latch onto details that have meaning for you today. Practice them.

I will leave you with this final imagery to help our imposter syndrome. You probably always see everyone’s code seems to look a bit different and you don’t know what “the best” really is. This is true for the same reason that if you told 5 artists to make a sculpture of a human head, all 5 of them will look different but all 5 of them will look like a nicely-done human head.

Your “created code” is no different. It will look different because you are a unique individual with unique strengths and weaknesses, but if you are appealing to the laws of mathematics and physics that the Universe revolves around, then your code probably looks great because form follows function. Good code looks like good code. Certain things are generally good, and certain things are generally bad.

The simplest solution tends to be the correct solution: look up the definition to the Occam's Razor. Go make a bunch of reuseable utility functions and compose them together at moments in time when you can use them. Remember to supply your functions with everything they need so they don’t need to reach outside themselves to operate correctly.

--

--

I prefer to work near ES6+, node.js, microservices, Neo4j, React, React Native; I compose functions and avoid classes unless private state is desired.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Adam Mackintosh

I prefer to work near ES6+, node.js, microservices, Neo4j, React, React Native; I compose functions and avoid classes unless private state is desired.