Clojure & JavaScript Higher-order Functions

Ona Staff
January 25, 2020

It was not easy for me to learn JavaScript. And when it came to learning higher-order functions, I had to take a break — literally get up and take a walk around the block — because I couldn’t seem to understand it. But through my pain, I’ve put together a short guide that hopefully makes it clearer for you.

Higher-order functions are functions that take other functions as arguments or return them as output, as opposed to normal functions that take as arguments and return non-functions. Functions that are passed as an argument to another function are what we refer to as callback functions. In this post we learn about higher-order functions through examples in JavaScript and Clojure, two functional languages with very different syntax.

Why are they necessary?

Flexibility! Higher-order functions allow you to change how a function behaves by adding your own behavior to its existing behavior. You are able to create something complex that is specific to your needs hence people refer to higher-order functions as a “powerful abstraction tool.”

In this post, I will focus on the three most commonly-used higher-order functions: filterreduce, and map.

Filter

This function takes a callback function and a collection. It returns a sequence in Clojure, or an array in JavaScript, containing the items in the collection which meet the condition of the function.

Example in Clojure


(def numbers [1 20 23 4 75 3])
(filter #(>= % 20) numbers)

;;; output
;;; (20 23 75)

Example in Javascript


const numbers = [1, 20, 23, 4, 75, 3];
const greaterNumbers = numbers.filter(num => {
    return num >= 20 });

console.log(greaterNumbers);

// output
// [20, 23, 75]

Map

Map takes a function and a collection. It then returns a lazy sequence in Clojure, or an array in JavaScript, that is the result of applying the function to each item in the collection.

Example in Clojure


(map #(* % 10) [1 2 3 4 5])

;;; output
;;; (10 20 30 40 50)

Example in Javascript


const numbers = [1, 2, 3, 4, 5]
const multipleNumbers = numbers.map(number => {
    return number * 10 });

console.log(multipleNumbers)

// output
// [10, 20, 30, 40, 50]

Reduce

Reduce also takes a callback function and a collection. The callback function should be a binary function, that is it should take two arguments. If on calling the callback is passed a value, the callback function takes that value and the first item in the collection to give a result. If it is no passed a value on the first iteration, the callback function begins by taking the first and second items in the collection as arguments, and returns a result. Let’s see this in action:

Example in Javascript

Without a value


const numbers = [1, 2, 3, 4];
const summedNums = numbers.reduce((accumulator, currentValue) => {
    return accumulator + currentValue });

console.log(summedNums);

// output
// 10

In the above example, the callback function took the first two items and added them together, then used the result of that operation as the first argument in the second iteration. The below type describes the steps in this iteration:

iteration-table

With a value


const numbers = [1, 2, 3, 4];
const summedNums = numbers.reduce((accumulator, currentValue) => {
    return accumulator + currentValue }, 100);

console.log(summedNums);

// output
// 110

In this case we pass 100 as the first argument to the callback function, and the iteration proceeds as in the table below:

iteration-table-100

Example in Clojure

Without a value


(reduce + [1 2 3 4])

;;; output
;;; 10

Here the + is the callback function that will take the first and second item and loop through.

With a value


(reduce + 100 [1 2 3 4])

;;; output
;;; 110

That is it! At the end of the day, higher-order functions are quite similar regardless of the programming language you are using. As long as you understand the general concepts and goals of higher-order functions in the language of your choice, you will be able to reapply that knowledge and use those same higher-order functions in other languages.

With this knowledge, I can now confidently create my own higher-order functions to write maintainable code in multiple languages. Higher-order functions allow me to write code that uses fewer lines while still being understandle. With higher-order functions the code is less repetitive and more efficient.

Tags