Functional Basics: First Class Functions

In functional programming, the concept of first-class functions is fundamental. A function is considered to be a first-class citizen if it can be treated like any other value. This means that, similar to other values, functions can be assigned to variables, stored in data structures, passed as arguments to other functions, and returned as values from other functions.

The ability to use functions as values leads to the concept of higher-order functions. A higher-order function is one that can take other functions as arguments, or return functions as results. This capability introduces a new level of abstraction, where not only values, but actions can be encapsulated and manipulated.

Consider, for instance, a function that accepts another function as an argument:

let apply f x = f x
let square x = x * x
let result = apply square 5  // result will be 25
F#

In the example above, apply is a higher-order function because it takes a function f as a parameter. This function f is then applied to x.

First class functions can also lead to functions being stored in structures. Take for instance this example in Dart of an list of functions:

var multiplyByTwo = (int x) => x * 2;
var functions = [multiplyByTwo, (int x) => x + 3];
Dart

A function can also return another function as a result. Consider this example where a function generates another function:

let addX x = (fun y -> x + y)
F#

In this case, addX returns a function that takes a parameter y and adds x to it. Here is the same in TypeScript, but with an application of the function following it's assignment:

function addX(x: number) {
  return function(y: number) {
    return x + y;
  };
}
const addFive = addX(5);
const result = addFive(3); 
TypeScript

And the exact same example again in TypeScript, but with lambda (arrow) syntax:

const addX = (x: number) => (y: number) => x + y
const addFive = addX(5);
const result = addFive(3); 
TypeScript

Since the idea of first class functions - specifically ones that return other functions - and their syntax can be a bit overwhelming at first if you are not used to it, now let's look at addX in a handful a languages:

// F#
let addX x = (fun y -> x + y)

// F# without lambda
let addX x y = x + y

// Dart
Function addX(x) {
  return (y) => x + y;
}

// TypeScript
const addX = (x: number) => (y: number) => x + y;

// Python
def addX(x):
    return lambda y: x + y

// C# (Using delegates)
Func<int, Func<int, int>> addX = x => y => x + y;

The concept of first-class functions gives way to other powerful functional programming techniques such as currying, function composition, and monads, all of which rely on the ability to manipulate functions as values. Eventually we will get to these concepts but on this journey we're being sure to take in all of the sights along the way.

Like expressions, utilizing functions as first-class citizens is something that's easy to start doing now, without any additional overhead and it's subtleties will slowly help you grok other concepts. Are you making a function that takes two parameters? Why not make it a function that takes one parameter that returns a function that takes the other parameter? A habit as simple as this is a great step! We can write functions and store them to named variables, pass them as arguments, and return them from functions. Depending on the language we can do this succinctly (in a language with first class functions) or verbosely (in a language like C# that fills the gap with concepts like delegates), but either way we're paving the road to composable, function-first software.