Higher Order Functions

In software development, a higher-order function is a function does does at least one of the following:

  • takes one or more functions as arguments
  • returns a function as a result.

Higher-order functions are essential to functional programming, a programming paradigm that emphasizes the use of functions as first-class citizens.

If you are familiar with JavaScript, the map(function) function is a higher-order function. It takes a function as its argument, applies the function to each element in the array and returns the modified array.

const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(x => x * 2);

console.log(doubled);  // Output is an array: [2, 4, 6, 8, 10]

Higher order functions allow us to express complex operations in a concise manner making the code more readable. Also they help reduce duplicate code because they are self-contained and can be used in multiple contexts. Looks explore this with an example.

Suppose we want to write a loop to iterate over some numbers and print the result to console. In imperative programming, we can write a function like this:

function repeat(n) {
    for (let i=0; i<n; i++) {
        console.log(i)
    }
}

Now suppose, instead of printing the result to the console, we want to apply some operation like multiplying the multiplying the number by 5 and then print the result. We can modify our function above to take a second parameter e.g. multiplyBy. What if we want to perform another operation e.g. % 2 on each number before print. You are getting the point. The code will become duplicative (if we create a new function for each action) or complex (we take many arguments each specifying the action we want to run. This will have function specify default values and use complex if-else logic to determine which action to run based on the arguments passed. The code will not be readable.)

In function programming, we can easily express our action as a function and pass it as an argument. Let’s write the code:


function repeat(n, action) {
    for (let i=0; i<n; i++) {
        action(i) // call the action function on each number
    }
}

repeat(5, console.log) // print each number up to 5. 
                       // Output: 0, 1, 2, 3, 4

repeat (5, function(i) {
    console.log(i*2) // 2x each number before printing. 
                     // Output: 0, 2, 4, 6, 8
})

Higher Order Functions in Programming Languages

Many programming languages support functional programming but some languages are more suited than others.

  • Haskell: Haskell is a pure functional programming language, which means that it does not have any side effects or mutable state. Haskell is known for its powerful type system and its support for higher-order functions.
  • Erlang: Erlang is a functional programming language designed for concurrency and fault tolerance. It is often used to build distributed systems and is known for its support for actor-based concurrency.
  • Scala: Scala combines functional and object-oriented programming. It is known for its support for higher-order functions and its use of the actor model for concurrency.

Java is an Object Oriented programming language and not a pure functional programming language. However, it does support some functional programming features. Java 8 took a big step in this direction by introducing lambda expressions, which allow you to write anonymous functions and use them as values. Java also has support for functional interfaces, which are interfaces that have a single abstract method and can be implemented using lambda expressions. For example,

public class Main {
  public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

    Function<Integer, Integer> doubleFunction = x -> x * 2;
    List<Integer> doubled = map(numbers, doubleFunction);
    System.out.println(doubled);  // Output: [2, 4, 6, 8, 10]
  }

  private static <T, R> List<R> map(List<T> list, Function<T, R> callback) {
    List<R> result = new ArrayList<>();
    for (T element : list) {
      result.add(callback.apply(element));
    }
    return result;
  }
}

In this example, the map function is a higher-order function because it takes a function as an argument. The doubleFunction is a lambda expression that takes an Integer as an argument and returns an Integer as a result. The function iterates over the element list and applies the doubleFunction to each element, storing the result in a new list which is returned at the end.


// 1. Higher Order Function Example in JavaScript
// Writing our own function like Array.map()

// myCustomMap takes an array and a callback function as arguments. It then iterates over 
// elements of the array and applies the callback function to each element, storing the 
// result in a new array which it returns
function myCustomMap(array, callback) {
  const result = [];
  for (const element of array) {
    result.push(callback(element));
  }
  return result;
}

const numbers = [1, 2, 3, 4, 5];
const doubled = myCustomMap(numbers, x => x * 2);

console.log(doubled);  // Output [2, 4, 6, 8, 10]


// -------
// Here's another example where we return a function from a function

// `createMultiplier` function takes a single argument multiplier and returns 
// a new function that takes a single argument x.
function createMultiplier(multiplier) {
  return function(x) {
    return x * multiplier;
  }
}

// When createMultiplier is called with a value for multiplier, it returns a 
// new function that can be used to multiply values by that amount.
const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15


import java.util.Arrays;
import java.util.function.IntUnaryOperator;

public class Main {
  public static void main(String[] args) {
    int[] numbers = {1, 2, 3, 4, 5};
    int[] doubled = applyOperation(numbers, x -> x * 2);
    System.out.println(Arrays.toString(doubled));  // Output: [2, 4, 6, 8, 10]
  }

  // This takes an array and a callback function (IntUnaryOperator) as arguments 
  // and applies the callback function to each element of the array. The modified 
  // elements are stored  in a new array that is returned at the end.
  private static int[] applyOperation(int[] array, IntUnaryOperator callback) {
    int[] result = new int[array.length];
    for (int i = 0; i < array.length; i++) {
      result[i] = callback.applyAsInt(array[i]);
    }
    return result;
  }
}

# Higher-order function. Takes two arguments: a number num and a function f.
# Tt returns the wrapper function, which can then be called with a value for x 
# to apply the function f to that value num times.
def apply_n_times(num, f):
    # wrapper function, a loop is used to apply f to x num times. 
    # The result of each application of f is stored in a variable result
    def wrapper(x):
        result = x
        for _ in range(num):
            result = f(result)
        return result
    return wrapper

def double(x):
    return x * 2

triple = apply_n_times(3, double)
print(triple(2))  # Output 16

#-----
# Here's another example that returns a function from a function.

# The higher-order function takes a single argument multiplier and returns a new function.
def create_multiplier(multiplier):
    # The multiply function takes a single argument x and returns the result of multiplying x by multiplier.
    # We'll return this function.
    def multiply(x):
        return x * multiplier
    return multiply

double = create_multiplier(2) # create a function called double that doubles its argument
triple = create_multiplier(3) # create a function called double that triples its argument

print(double(5))  # Output: 10
print(triple(5))  # Output: 15
package main

import "fmt"

func applyTwice(f func(int) int) func(int) int {
	return func(x int) int {
		return f(f(x))
	}
}

func main() {
	double := func(x int) int {
		return x * 2
	}

	twice := applyTwice(double)
	fmt.Println(twice(2)) // 8
}


Related Videos