Posted on by & filed under Content - Highlights and Reviews, Programming & Development.

A guest post by Timothy Pratley, who currently works for Tideworks Technology as a Development Manager building Traffic Control software for logistics clients including SSA Marine, CSX, and BNSF.

In this post, I will cover loops from a functional programming perspective, pointing out the advantages of using this approach. Imperative style loops will be contrasted with sequence functions to illustrate why I prefer sequence abstractions over loops, and all examples will of course be in Clojure.

## Overview

LINQ is a huge success because sequences are such a powerful abstraction, providing terse, yet meaningful syntax and compositional leverage. Clojure implements a similar sequence abstraction that is carefully aligned with other language features such as laziness and immutability. You will see in this post how Clojure sequences are terse, meaningful, and help avoid common errors that loops suffer from.

## Simple Sequences

Clojure has an excellent sequence abstraction that fits naturally into the language. From a vector `[1 2 3 4]` we can find the odd numbers by calling the `filter` function:

Here we called the `filter` function with two arguments: the `odd?` function and a vector of integers. `filter` is a higher order function, since it takes an input function to use in its computation. The result is a sequence of odd values. Functions like `filter` that operate on sequences call `seq` on their arguments to convert collections to sequences. The underlying mechanism is the `ISeq` interface, which allows many collection data structures to provide access to their elements.

`map` is a function that calls another function for every element in a sequence:

The result is a sequence of the increment of each number in `[1 2 3 4]`.

Sequences can be used as input arguments to other functions as shown here:

Here we filtered by `odd?` the values from `(2 3 4 5)`, which was the result of calling `map`.

To aggregate across a sequence, use `reduce`:

For each element in the sequence, `reduce` computes (`* aggregate element`) and passes the result of that as the aggregate for the next calculation. The first element `1` is used as the initial value of `aggregate`. The final result is `1 * 2 * 3 * 4`.

Clojure provides a built-in function for grouped aggregates:

3 letter words are “the” and “fox,” whereas 5 letter words are “quick” and “brown.”

Let’s compare this to SQL or LINQ:

• `filter` is like where
• `map` is like select
• `reduce` is like single value aggregation
• `group-by` is like group by aggregation

And if we compare to an imperative style:

• `filter` is like:

• `map` is like:

• `reduce` is like:

Sequence abstractions are like names for loops that you can add to your vocabulary to talk about and recognize different kinds of loops. Learning the names of the abstractions and patterns that replace loops is an effort, but it adds powerful words to a programmer’s vocabulary. A large vocabulary facilitates reasoning more succinctly, communicating more effectively, and writing less code that does more.

Clojure provides a special form `#()` to create an anonymous function:

The `%` symbol is an implied input argument. This function takes one argument and returns true if the input argument is less than 3, otherwise it is false. Anonymous functions are handy for adding small snippets of logic:

This, of course, keeps only numbers less than 3. The following creates a sequence of odd/even strings for each number in the vector:

Sequence abstractions are more concise and descriptive than loops, especially when filtering multiple conditions, or performing multiple operations.

Clojure also has useful functions for constructing sequences:

## Difficult Sequences

One situation that appears difficult to use a sequence abstraction in is when we have a vector of numbers and wish to perform a sequence operation that relies upon the previous value visited. For example, think about finding the sum of each pair in `[1 2 3 4 5]`. Using an imperative style loop we can peek into the vector at the previous value:

Can we represent this as a sequence? Yes! Imagine two identical sequences offset slightly:

The overlapping values are the pairs we want.

`map` can take multiple sequences from which to pull arguments for the input function:

Here, 1 adds to 2 to make 3, and 3 adds to 4 to make 7.

`rest` is a function which returns the input sequence without its first element:
(def v [1 2 3 4 5])
(rest v)
-> (2 3 4 5)

Putting them together:

We called map on the addition function over both input sequences:

The input sequences were of different lengths, so `map` stopped when the smallest sequence was exhausted. The result was a new sequence of the pairwise sums:

So why are sequence abstractions better than loops? When reading a loop you must comprehend the entire block of code to know what it does. As the loop body grows and changes you must mentally keep track of more complexity. Mistakes like “off by one” are hard to spot, and can creep in as the code changes. Testing requires the invasion of the loop with breakpoints. You may find yourself duplicating a loop to customize some similar operation. The loop abstraction is very easy to understand and use, but it does not provide leverage.

Imagine discovering a new requirement where you need to multiply all of those numbers together. The change is invasive to the imperative loop:

The change occurs inside the loop with the addition and multiplication intertwined.

Contrast this with modifying the Clojure sequence. We compose a `reduce` with the original `map` expression:

• `reduce` step: Aggregate by multiplication the sequence made from the following:
• `map` step: adding items together from two sequences
• `pairing`: the sequence of elements in `v`, adjacent to the sequence of skipping the first element of `v`

This is dense, but descriptive code, if you know the vocabulary.

With a sequence you can write unit tests for the component sequences and operations, reuse the same sequence without writing new code, and reason about the transformations as composable parts.

By now, you should be feeling the combinatorial power functions offer. Simple functions compose sequence operations together to build transforms. Clojure has almost one hundred functions related to sequences, so you should also be feeling wary of such dense code. If we keep adding layers of function calls, the code becomes cryptic:

With three layers of function calls, things are getting hard to keep in our head all at once. This expression may be easier to mentally process by starting from the innermost `map`, working out to `filter`, and then out to `reduce` last. But that is the opposite of our reading direction and locating the true starting point is difficult.

The presentation of sequence operations is clearer if you name intermediary results:

Or use a pipeline style:

Look out for opportunities to name your steps by identifying long expressions and creating a named function out of them. Named functions are declared like this:

When writing a long expression, pay attention to presentation such that a reader can focus on one operation at a time. You will find it much easier to test parts of your expression and create unit tests when the component steps are clearly delimited in a logical order.

## Conclusion

Clojure exposes a sequence interface over data collections to a rich set of functions that compose well. Three important functional sequence concepts are: `filter`, which retains each item in a sequence where some function evaluates to true; `map`, which selects new values by calling a function over input sequence(s) to create a new sequence; and `reduce`, which aggregates a sequence and returns a single value. As we have explored in this post, sequence abstraction has strong advantages over looping.

I invite you to take the “no loops” challenge. The next time you spot a loop, stop and think about what sequence operation the loop represents. Think about how to rewrite the loop as sequence operations instead. It will take time and mental effort, but you will be rewarded with a deeper understanding of the problem being solved.

If you are new to Clojure and want to learn more of the language please check out these Clojure books available from Safari Books Online:

## Safari Books Online has the content you need

 Clojure Programming, helps you learn the fundamentals of Clojure with examples relating it to the languages you know already—whether you’re focused on data modeling, concurrency and parallelism, web programming, statistics and data analysis, and more. Practical Clojure is the first definitive reference for the Clojure language, providing both an introduction to functional programming in general and a more specific introduction to Clojure’s features. This book demonstrates the use of the language through examples, including features such as STM and immutability, which may be new to programmers coming from other languages. The Joy of Clojure goes beyond the syntax, and shows how to write fluent, idiomatic Clojure code. You will learn to approach programming challenges from a Functional perspective and master the Lisp techniques that make Clojure so elegant and efficient. This book will help you think about problems the “Clojure way,” and recognize when they simply need to change the way they program.