Clojure

Higher Order Functions

First Class Functions

In functional programming, functions are first class citizens. This means functions can be treated as values. They can be assigned as values, passed into functions, and returned from functions.

It’s common to see function definitions in Clojure using defn like (defn foo …​). However, this is just syntactic sugar for (def foo (fn …​)) fn returns a function object. defn returns a var which points to a function object.

Higher Order Functions

A higher order function is a function that either:

  1. Takes one or more functions as arguments

  2. Returns a function as its result

This is an important concept in functional programming in any language.

Higher order functions allow us to compose functions. This means we can write small functions and combine them to create larger functions. Like putting a bunch of small LEGO bricks together to build a house.

Let’s move away from theory a bit and look at an example.

Functions as Arguments

Let’s look at two functions

(defn double-+
    [a b]
    (* 2 (+ a b)))
(defn double-*
    [a b]
    (* 2 (* a b)))

These functions share a common pattern. They only differ in name and the function used in the computation of a and b. In general, the pattern looks like this.

(defn double-<f>
    [a b]
    (* 2 (f a b)))

We can generalize our double- function by passing f in as an argument.

(defn double-op
    [f a b]
    (* 2 (f a b)))

We can use this to express the concept of doubling the result of an operation rather than having to write functions that perform specific doubled operations individually.

Function Literals

An anonymous function is a function without a name. In Clojure these can be defined in two ways, fn and the literal #(…​). Creating a function with defn immediately binds it to a name, fn just creates a function.

Lets go back to the example of the bands. We wrote a function called rock-band? to determine whether or not our band was a rock band. This is a one-off operation, we’re not going to use it anywhere else in our code. We can save ourselves some keystrokes by using an anonymous function.

(def rock-bands
    (filter
        (fn [band] (= :rock (:genre band)))
        bands))

Even more concisely, using the function literal, we can define rock-bands like so.

(def rock-bands (filter #(= :rock (:genre %)) bands))

The function literal supports multiple arguments via %, %n, and %&.

#(println %1 %2 %3)

If you’re writing an anonymous function, the literal syntax is nice because it’s so compact. However, beyond a few arguments, the syntax can become difficult to read. In that case, using fn may be more appropriate.

Functions Returning Functions and Closures

Our first function will be called adder. It will take a number, x, as it’s only argument and return a function. The function returned by adder will also take a single number, a, as it’s argument and return x + a.

(defn adder [x]
  (fn [a] (+ x a)))

(def add-five (adder 5))

(add-five 100)
;; => 105

The returned function form adder is a closure. This means, it can access all of the variables that were in scope when the function was created. add-five has access to x even though it is outside of the adder function definition.

Filter

Filtering is a common operation in computer programming. Take this set of animals

(def pets [
    {:name "Fluffykins" :type :cat}
    {:name "Sparky" :type :dog}
    {:name "Tibby" :type :dog}
    {:name "Al" :type :fish}
    {:name "Victor" :type :bear}
])

We want to filter out the non-dog animals because we’re writing enterprise grade software. First, let’s look at a normal for loop.

(defn loop-dogs [pets]
    (loop [pets pets
           dogs []]
        (if (first pets)
            (recur (rest pets)
                   (if (= :dog (:type (first pets)))
                       (conj dogs (first pets))
                       dogs))
            dogs)))

This code works fine, but it’s bulky and confusing. We can simplify this using filter, a higher order function.

(defn filter-dogs [pets]
    (filter #(= :dog (:type %)) pets))

The solution using filter is much clearer and allows us to show intent rather than just give commands. We can break this into even smaller pieces by breaking the filtering function out into a separate var.

(defn dog? [pet] (= :dog (:type pet)))

(defn filter-dogs [pets] (filter dog? pets))

Original author: Michael Zavarella