The JS Bifrost — Efficient Code with JS functions

Understanding Pure and Higher-Order function to write state-of-the-art code!

Ashmit Bhatnagar
Globant
7 min readSep 3, 2020

--

Welcome to The JS Bifrost, your pathway to rock solid foundation for a God-level JavaScript. This is the first article in the series. This article is all about how you can leverage JavaScript functions to produce efficient code.

The craft of programming is the factoring of a set of requirements into a set of functions and data structures.

Functions are one of the key components in programming. They are defined to perform a specific task and to perform it again and again whenever called. Simple and straightforward, right?

But…

The best thing about JavaScript is its implementation of functions. It got almost everything right. But, as you should expect with JavaScript, it didn’t get everything right. — Douglas Crockford, JavaScript: The Good Parts

JavaScript has always been this way!!

Let's explore functions with a very practical perspective so that we are a step ahead in leveraging the power of JavaScript.

Functions are majorly used for:

  • Action: A function is used to perform a sequence of actions that are redundant in our code. Hence it is also called as a Procedure. It can be called any number of times at the place where that particular procedure ( sequence of actions ) needs to be executed.
  • Association: A function associates (maps) input given to an output based on the actions that it is designed to perform.
  • Inter-communication: A function can also be used to interact with other parts of the systems using input and output operations.

Having understood the use of a function, let’s talk about two types of functions that you should know for writing cleaner and efficient code.

Pure Functions

A pure function:

  • Always gives you the same output for the same input.
  • Creates no side effects.

If these two points are satisfied by a function it is a Pure function.

Hence, Pure function is all about Mapping. It will take input and give you the same output if the input is repeated, else it will give you the corresponding output. This means that, if there exists a set of outputs for a set of inputs, Pure function maps input to the output.

A pure function is independent of any state, or data change during a program’s execution. It must only depend on its input.

Let’s understand pure function with an example.

Below is a simple greeting function that takes the name of the person as input.

const greeting = "Hello"const greet = person => `${greeting} ${person}!`;console.log(greet("Ashmit")); // "Hello Ashmit!"

You may think that this will give the same output on the same input every time! Yes, that’s correct!

But it is not independent of whatever is happening outside the function. If we change the variable ‘greeting’ to “Howdy” the output will change even if we didn’t change the input to the ‘greet’ function.

const greeting = "Howdy"const greet = person => `${greeting} ${person}!`;console.log(greet("Ashmit")); // "Howdy Ashmit!"

A pure function should be independent of any external mutable state. It should only depend on its input. The above scenario is making our function impure because of its interaction with the outside mutable world, which is referred to as a Side Effect.

Let’s see in detail what the side effects actually mean with respect to pure functions.

Side Effect

A side effect is any interaction with the outside world from within a function. That could be anything from changing a variable that exists outside the function or calling another method from within a function to accessing a variable from outside the function.

Note: If a pure function calls a pure function this isn’t a side effect and the calling function is still pure.

Let's make the impure ‘greet’ function pure, with a slight modification. We know that pure function should only be dependent on the inputs and should work the same even if the outside world is on fire. So we’ll do exactly that,

const greeting = "Hello"const greet = (greeting, person) => `${greeting} ${person}!`;console.log(greet(greeting, "Ashmit"));// "Hello Ashmit!"

Now our function ‘greet’ is pure as it only depends on its inputs. It will always give the same output with the same input.

Benefits of Pure functions

  • Pure functions are immediately testable once created and are also helpful in maintaining and refactoring code.
  • The outputs of pure functions can be memoized as they will always give the same output for the same input. This proves very helpful in Dynamic Programming where optimal substructure is a must.

One very important and use-case of pure functions is in Redux ( State management for ReactJS ). The reducers must always be pure functions because reducers perform actions on the state. If reducers are impure for the same state of actions on the state of an application, it will yield different results and hence will fail its purpose!
( By the way, if you don’t know ReactJS yet, I highly recommend you to learn it )

Higher-Order Functions

Higher-Order Functions are extensively used in JavaScript. If you have been programming in JavaScript for a while, you may have already used them without even knowing.

In JavaScript, functions are First Class citizens and by first class, I mean that functions are treated as values. So they can be passed around as values as well.

Higher-Order Functions are functions that take on other functions, either by taking them as arguments or by returning them. Since JavaScript supports first-class functions, we leverage this facility to create Higher-Order functions.

Higher-order functions allow us to abstract over actions, not just values.

Taking another function as an argument is often referred to as a callback function because it is called back by the higher-order function. This is a concept that JavaScript uses a lot.

Let’s understand the Higher-Order function with an example of a function that’s used very often when dealing with arrays - Array.prototype.map().

Map() returns a new array populated with the results of calling a provided function (callback) on every element in the calling array. Basically, map() takes a function as an input and executes that function on every element of the array. It returns a new array filled with the resulting values.

Let’s see this with a simple example.

const double = n => n * 2; // Callback functionconsole.log([1, 2, 3, 4].map(double)); // [2, 4, 6, 8]

Here, map() takes ‘double’ function as the input making it a higher-order function. Similarly, if a function returns a function, it will be a higher-order function.

Benefits of using Higher-Order functions

  • We can create smaller functions that only take care of one piece of logic.
  • We can compose complex functions by using different smaller and simpler functions. This increases code reusability reduces bugs and makes our code easier to read and understand.

Like Pure functions, Higher-Order functions are also used in Redux. A reducer is simply a function, so when we apply this pattern to reducers, we call them higher-order reducers.

When you have to duplicate logic in each of your reducers with minor changes, it becomes really cumbersome. Higher-Order reducers help us in reducing our load, by returning a reducer in the default case of the switch. This way we can proxy the call to the original reducer. As reducers are Pure functions, this creates a beautiful combination, resulting in a block that handles a complex code with all the benefits of pure functions, but yet is higher-order.

Closing Thoughts

Writing clean and efficient code is the need of the hour. Our code should be precise and effective. Utilizing a language’s features to its maximum is a challenge in itself.

Writing clean code is what you must do in order to call yourself a professional. There is no reasonable excuse for doing anything less than your best.
Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

Pure functions and Higher-Order functions are two such features of JavaScript that take function to the next level, providing us with the tools to deliver crisp, clean and efficient code. We must try to incorporate these in our code as much as possible to improve the quality of the code that we write.

Watch this space to make more progress on your way to ‘God level JavaScript’ with ‘The JS Bifrost’.

--

--