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.


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.


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.

About the author

timhead Timothy Pratley currently works for Tideworks Technology as a Development Manager building Traffic Control software for logistics clients including SSA Marine, CSX, and BNSF. He can be reached at

Tags: Clojure, LINQ, loops, Sequence Abstractions, SQL,

Comments are closed.