Rifle-Oriented Programming with Clojure

Posted by: Stuart Halloway on August 12, 2009

Any comparison of hot JVM languages is likely to note that “Clojure is not object-oriented.” This is true, but it may lead you to the wrong conclusions. It’s a little like saying that a rifle is not arrow-oriented. In this article, you will see some of the ways that Clojure addresses the key concerns of OO: encapsulation, polymorphism, and inheritance.

This is a whirlwind tour, and we won't have time to cover the full details of all the Clojure code you will see. When we are done, I hope you will decide to explore for yourself. You can download and start using Clojure by following the instructions on the getting started page.

Just Enough Clojure Syntax

Clojure has <emph>vectors</emph>, which are accessed by integer indexes:

[1 2 3 4]
-> [1 2 3 4]

(get [:a :b :c :d :e] 2)
-> :c

In the preceding example, the initial [1 2 3 4] is input that you enter at the Read-Eval-Print Loop (REPL). The -> indicates the response from the REPL.

Clojure has <emph>maps</emph>, which are key/value collections:

{:fname "Stu", :lname "Halloway"}
-> {:fname "Stu", :lname "Halloway"}

<emph>Sets</emph> contain a set of values, and their literal form is preceded with a hash. Here is the set of English vowels, using backslash to introduce a character literal:

#{\a \e \i \o \u}
-> #{\a \e \i \o \u}

Lists are singly-linked lists, and are enclosed with parentheses. Lists are special: Not only are they data, they also act as the syntax for invoking functions. The list below invokes the plus (+) function:

(+ 1 2 3 4 5)
-> 15

Collections themselves act as functions. They take an argument which is the key/index to look up:

([:a :b :c :d :e] 2)
-> :c

({:name "Stu" :ext 101} :name)
-> "Stu"

Enough syntax, let's get started.


Encapsulation is the hiding of implementation details so that clients of your code do not accidentally become dependent on them. In object-oriented languages, this is ususally done at the class level. A class has public methods, private implementation details, and various other scopes in between.

Clojure accomplishes the purposes of encapsulation in three ways: closures, namespaces, and immutability.


A closure closes over (remembers) the environment at the time it was created. For example, the function make-counter below closes over the initial value passed via init-val:

(defn make-counter [init-val] 
  (let [c (atom init-val)] #(swap! c inc)))

Let’s break this down:

  • defn defines a new function, named make-counter, that takes a single argument init-val.
  • The let binds the name c to a new atom.
  • The atom creates a threadsafe, deadlock-proof mutable reference to a value.
  • The octothorpe (#) prefix introduces an anonymous function
  • The call to swap! updates the value referenced by c by calling inc on it.
  • The value of the let is the value of its last expression. This let returns a function that increments a counter, which is then the return value of make-counter.

The atom c is private to the function returned by make-counter. The only public thing you can do is increment it by one:

(def c (make-counter))
-> #'user/c

-> 1

-> 2

-> 3

The counter example returned a single function, but nothing stops you from returning multiple functions. These multiple functions can then share private state. The new version of make-counter below returns two functions: one to increment the counter, and one to reset it.

(defn make-counter [init-val] 
  (let [c (atom init-val)] 
    {:next #(swap! c inc)
     :reset #(reset! c init-val)}))

This new make-counter returns a map whose :next value increments the counter, and whose :reset value resets it:

(def c (make-counter 10))
-> #'user/c

((c :next))
-> 11

((c :next))
-> 12

((c :reset))
-> 10

Why the double parentheses above? Two functions calls: The inner function call looks up the appropriate function, and the outer one calls it.

Closing over data is far more general than the simplistic model offered by private, protected, public, friend, et al. in OO languages. By combining multiple lets and multiple return values from a function, you can create arbitrary encapsulation strategies.

Similar encapsulation possibilities are available in any language that supports closures. Douglas Crockford describes a similar idiom in JavaScript.


A Clojure namespace groups a set of related data and functions. Inside a namespace, a Clojure var can refer to a function or to data, and can be public or private.

For example, Chris Houser’s error-kit library implements a condition/restart system for Clojure.

  (vec (map int-half [2 4 5 8]))
    (handle *number-error* [n]
      (continue-with 0))) 

In the code above, with-handler, handle, and continue-with are public vars of the clojure.contrib.error-kit namespace. The int-half is a demo function that blows up on odd inputs. When a *number-error* occurs, the handler causes execution to continue with the value 0. (Note how this is more flexible than try/catch exception handling, which cannot recover back into the middle of some operation.)

Internally, error-kit keeps track of available handlers and continues using these private vars:

(defvar- *handler-stack* () 
  "Stack of bound handler symbols")
(defvar- *continues* {} 
  "Map of currently available continue forms")

The trailing minus sign on the end of defvar- marks the vars as private. These vars are implementation details, and are invisible to code outside the clojure.contrib.error-kit namespace.


In OO languages, another purpose of encapsulation is to prevent object A from modifying or corrupting the private data used by object B.

In Clojure, this problem does not exist. Data structures are immutable. They cannot possibly be corrupted, or changed in any way, period. You can write query functions that return “private” state, without any fear of data corruption.


For our purposes here, polymorphism is the ability to choose a different method implementation based on the type of the caller. So for example:

Flyer a = new Airplane();
Flyer b = new Bird();

a.fly() and b.fly() do different things because they are called on different concrete types.

Clojure provides a generalization of polymorphism called multimethods. A multimethod definition begins with defmulti, and then has a name, plus a dispatch function that is used to select the actual implementation: To mimic polymorphism, simply dispatch on the class of the argument:

(defmulti fly class)

Individual methods of a multimethod begin with defmethod, then the multimethod name, then the object that must match the dispatch function. Finally, you get the argument list in a vector, followed by the implementation of the method. For example:

(defmethod fly Bird [b] (flap-wings b))
(defmethod fly Airplane [a] (turn-propeller a))

Unlike polymorphism, multimethods do not limit you to dispatching on class. You can dispatch based on any arbitrary function of the method arguments. So for example, a bank account might have a :type entry that is used to determine the interest rate:

(defmulti interest :type)
(defmethod interest :checking [a] 0)
(defmethod interest :savings [a] 0.05M)

The :type attribute is a convention, but nothing prevents you from dispatching on a different attribute, or even dispatching on more than one at the same time! For example, the service-charge multimethod below dispatches on two different facets of the same object: the object’s account-level (::Basic or ::Premium) and its :tag: (::Checking or ::Savings)

(defmulti service-charge 
  (fn [acct] [(account-level acct) (:tag acct)]))
(defmethod service-charge [::Basic ::Checking]   [_] 25)
(defmethod service-charge [::Basic ::Savings]    [_] 10)
(defmethod service-charge [::Premium ::Checking] [_] 0)
(defmethod service-charge [::Premium ::Savings]  [_] 0)

The _ is a legal name, and is used idiomatically to indicate that an argument will be ignored. (There is no need to even look at the argument, since all the work has been done in choosing which method to dispatch to!) This example also demonstrates two other concepts:

  • The double-colon prefix resolves a keyword in a namespace. This prevents name collisions among keywords, just as object-oriented langauges use namespaces to prevent name collisions between type names.
  • account-level is a function (not shown here), not a simple key lookup. It returns ::Premium or ::Basic based on the the account type and the current balance. Thus an account can dynamically change its account level as its balance changes.

As you can see, multimethods are far more general than polymorphism. Instead of being limited to type-based dispatch, multimethods can dispatch on any arbitrary function of an argument list. This allows programming models that more closely resemble reality: after all, what real-world entities are limited to a single type hierarchy, and forbidden to change types over time?


In OO languages, inheritance allows you to create a derived type that reuses the behavior of a base type. For example:

class Person {
  String fullName() { /* impl details */ }
class Employee extends Person {
  AddressBookItem companyDirectoryEntry() { /* impl details */ }

This kind of reuse is so natural in Clojure that it doesn’t even have a name. For example, here is a function that returns the full name of a person, based on first and last names:

(defn full-name [p]
  (str (:first-name p) " " (:last-name p)))

Employees are like people, but have other properties and behaviors, such as a telephone extension. The company-directory-entry returns a vector of an employee's full name and telephone extension, like this:

(defn company-directory-entry [p]
  [(full-name p) (:extension p)])

Notice that company-directory-entry “reuses” the person-ness of its argument p by calling full-name on it. There is no special inheritance ceremony required to set this up, you just call functions when you need them.

You can pass either a person or an employee to full-name. For company-directory-entry, though, you must have an employee. Or, more accurately, you must have something that resembles an employee, to the extent of having a :first-name, :last-name, and :extension. This is an example of duck typing: if it walks like a duck and quacks like a duck, we assume it is a duck, without asking it to present its IDuck papers.

Many Functions, Few Types

The example above demonstrates another negative consequence of idiomatic OO style: the over-specification of data types. The return value of companyDirectoryEntry is given its own unique type, AddressBookItem. Each new data type like AddressBookItem requires its own life-support system: constructors, accessors, equals, hashCode, and so on.

In Clojure, an address book item would simply be a vector or a map. No new types, and no life support system required. Moreover, an address book item can be manipulated with any of the large arsenal of functions in Clojure's sequence library.

To see the problem with overspecifying types, consider this method from the Apache Commons:

// From Apache Commons Lang, http://commons.apache.org/lang/
public static int indexOfAny(String str, char[] searchChars) {
    if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) {
	return -1;
    for (int i = 0; i < str.length(); i++) {
	char ch = str.charAt(i);
	for (int j = 0; j < searchChars.length; j++) {
	    if (searchChars[j] == ch) {
		return i;
    return -1;

The purpose of indexOfAny is to find the index of the first occurrence of one of the searchChars that appears in str. Note the unnecessary specificity of types: it works only with strings and character arrays.

Here's the Clojure version, using the sequence library's map, iterate, and for forms:

(defn indexed [coll] (map vector (iterate inc 0) coll))
(defn index-filter [pred coll]
  (when pred 
    (for [[idx elt] (indexed coll) :when (pred elt)] idx)))

Here is an example calling index-filter:

(index-filter #{\a \e \i \o \u} "Lts f cnsnts nd n vwel")
-> (20)

The expression above finds the index of the first vowel in the string "Lts f cnsnts nd n vwel", that is, 20. But index-filter is more general than the Commons version in several ways:

1. index-filter returns all the matches, not just one.

(index-filter #{\a \e \i \o \o} "The quick brown fox")
-> (2 6 12 17)

2. index-filter works with any sequence, not just a string of characters. For example, the call below works against a range of integers:

(index-filter #{2 3 5 7} (range 6))
-> (2 3 5)

3. index-filter works with any predicate, not just a test against a character array. In the example below, the predicate is an anonymous function that tests for strings longer than three characters:

(index-filter #(> (.length %) 3) ["The" "quick" "brown" "fox"])
-> (1 2)

That is a lot of extra power, especially given that the function is shorter, easier to write, and easier to read (given some Clojure experience, of course) than the Commons version.


Clojure solves the same problems that OO solves, but it solves them in different ways. Instead of encapsulation, polymorphism, and inheritance, you have closures, namespaces, pure functions, immutable data, and multimethods. Idiomatic OO gives you a bloated type system with duplicated code hidden away behind encapsulation boundaries and little hope for thread safety. Clojure offers a radical alternative: a lean type system, a rich function library, and language-level concurrency support that is usable by mere mortals.

There is a lot more to Clojure than we have covered here: lazy and infinite sequences, destructuring, macros, software transactional memory, agents, seamless Java interop, and more. But those are topics for another day.

[This article was originally published in the May 2009 issue of NFJS, the Magazine. I will be speaking about Clojure at several upcoming NFJS events, come join the fun.]

Stuart Halloway

About Stuart Halloway

Stuart Halloway is a founder and President of Cognitect, Inc. (www.cognitect.com). He is a Clojure committer, and a developer of the Datomic database.

Stuart has spoken at a variety of industry events, including StrangeLoop, Clojure/conj, EuroClojure, ClojureWest, SpeakerConf, QCon, GOTO, OSCON, RailsConf, RubyConf, JavaOne, and NFJS.

Stuart has written a number of books and technical articles. Of these, he is most proud of Programming Clojure.

Learn more about Stu's presentations on his wiki.

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »