O'Reilly logo

Clojure Programming by Brian Carper, Christophe Grand, Chas Emerick

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

The Clojure REPL

Many languages have REPLs, often also referred to as interpreters: Ruby has irb; Python has its command-line interpreter; Groovy has its console; even Java has something akin to a REPL in BeanShell. The “REPL” acronym is derived from a simple description of what it does:

  1. Read: code is read as text from some input (often stdin, but this varies if you’re using a REPL in an IDE or other nonconsole environment).

  2. Eval: the code is evaluated, yielding some value.

  3. Print: the value is printed to some output device (often stdout, sometimes preceded by other output if the code in question happened to print content itself).

  4. Loop: control returns to the read step.

Clojure has a REPL too, but it differs from many other languages’ REPLs in that it is not an interpreter or otherwise using a limited or lightweight subset of Clojure: all code entered into a Clojure REPL is compiled to JVM bytecode as part of its evaluation, with the same result as when code is loaded from a Clojure source file. In these two scenarios, compilation is performed entirely at runtime, and requires no separate “compile” step.[2] In fact, Clojure is never interpreted. This has a couple of implications:

  1. Operations performed in the REPL run at “full speed”; that is to say, there is no runtime penalty or difference in semantics associated with running code in the REPL versus running the same code as part of a “proper” application.

  2. Once you understand how Clojure’s REPL works (in particular, its read and eval phases), you’ll understand how Clojure itself works at the most fundamental level.

With this second point in mind, let’s dig into the Clojure REPL and see if we can find bedrock.

Note

The optimal workflow for programming in Clojure makes much more use of the REPL than is typical in other languages to make the development process as interactive as possible. Taking advantage of this is a significant source of the enhanced productivity—and really, fun!—that Clojure enables. We talk about this extensively in Chapter 10.

Example 1-1. Starting a Clojure REPL on the command line

% java -cp clojure-1.4.0.jar clojure.main
Clojure 1.4.0
user=>

This incantation starts a new JVM process, with a classpath that includes the clojure.jar file in the current directory, running the clojure.main class as its main entry point.[3] See A classpath primer if you don’t yet know what the classpath is; for now, you can just think of the classpath as the JVM’s analogue to Python’s PYTHONPATH, Ruby’s $:, and your shell’s PATH, the set of files and directories from which the JVM will load classes and resources.

When you see the user=> prompt, the REPL is ready for you to enter some Clojure code. The portion of the Clojure REPL prompt preceding => is the name of the current namespace. Namespaces are like modules or packages; we discuss them extensively later in this chapter in Namespaces. Clojure REPL sessions always start in the default user namespace.

Let’s look at some real code, a function that calculates the average of some numbers in Java, Ruby, and Python:

Example 1-2. Averaging numbers in Java, Ruby, and Python

public static double average (double[] numbers) {
  double sum = 0;
  for (int i = 0; i < numbers.length; i++) {
    sum += numbers[i];
  }
  return sum / numbers.length;
}

def average (numbers)
  numbers.inject(:+) / numbers.length
end

def average (numbers):
    return sum(numbers) / len(numbers)

Here is the Clojure equivalent:

(defn average                            1
  [numbers]                              2
  (/ (apply + numbers) (count numbers))) 3
1

defn defines a new function named average in the current namespace.

2

The average function takes one argument, referred to within its body as numbers. Note that there is no type declaration; this function will work equally well when provided with any collection or array of numbers of any type.

3

The body of the average function, which sums the provided numbers with (apply + numbers),[4] divides that sum by the number of numbers provided—obtained with (count numbers)—and returns the result of that division operation.

We can enter that defn expression at the REPL, and then call our function with a vector of numbers, which yields the expected result:

user=> (defn average
         [numbers]
         (/ (apply + numbers) (count numbers)))
#'user/average
user=> (average [60 80 100 400])
160


[2] If necessary, you can ahead-of-time compile Clojure to Java class files. See Ahead-of-Time Compilation for details.

[3] Alternatively, you can use java -jar clojure.jar, but the -cp flag and the clojure.main entry point are both important to know about; we talk about both in Chapter 8.

[4] Note that + here is not a special language operator, as in most other languages. It is a regular function, no different in type than the one we’re defining. apply is also a function, which applies a function it is provided with to a collection of arguments (numbers here); so, (apply + [a b c]) will yield the same value as (+ a b c).

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required