(defmacro resolution [] `x)

Viewing the expansion of this macro is illuminating in understanding how Clojure

macros resolve symbols:

(macroexpand '(resolution))

;=> user/x

The expansion of the macro resolves the namespace of the syntax-quoted symbol x.

This behavior is useful in Clojure by helping to avoid free name capturing problems

that are possible in a macro system such as that found in Common Lisp.8 Here’s an

example that would trip up a lesser implementation of syntax-quote, but which does

just what we want in Clojure:

(def x 9)

(let [x 109] (resolution))

;=> 9

8

Among one of the ways that Common Lisp works to alleviate this kind of problem is the use of gensym. The

key difference is that in Common Lisp, you have to be careful to avoid name capturing, whereas Clojure avoids

it by default.

Download from Wow! eBook <www.wowebook.com>

170

CHAPTER 8 Macros

The x defined in the let isn’t the same as the namespace-qualified user/x referred to

by the macro resolution. As you might expect, the macro would’ve thrown an

unbound Var exception had we not first executed the call to def.

Clojure does provide a way to defer symbolic resolution for those instances where

it may be useful to resolve it within the execution context, which we’ll show now.

8.5.1

Anaphora

Anaphora9 in spoken language is a term used in a sentence referring back to a previ-

ously identified subject or object. It helps to reduce repetition in a phrase by replac-

ing “Jim bought 6,000 Christmas lights and hung all of the Christmas lights,” with “Jim

bought 6,000 Christmas lights and hung them all.” In this case, the word them is the

anaphora. Some programming languages use anaphora, or allow for their simple defi-

nition. Scala has a rich set of anaphoric (Odersky 2008) patterns primarily focused

around its _ operator:

Array(1, 2, 3, 4, 5).map(2 * _)

//=> res0: Array[Int] = Array(2, 4, 6, 8, 10)

In this Scala example, the underscore serves to refer back to an implicitly passed

argument to the map function, which in this case would be each element of the array

in succession. The same expression could be written with (x) => 2 * x—the syntax for

an anonymous function—in the body of the map call, but that would be unnecessarily

verbose.

Anaphora don’t nest, and as a result are generally not employed in Clojure. Within

a nested structure of anaphoric macros, you can only refer to the most immediate ana-

phoric binding, and never those from outer lexical contours, as demonstrated in list-

ing 8.7. For example, the Arc programming language (Graham Arc) contains a macro

named awhen similar to Clojure’s when, save that it implicitly defines a local named it

used within its body to refer to the value of the checked expression.

Listing 8.7 An example of anaphora and its weakness

(defmacro awhen [expr & body]

`(let [~'it ~expr]

Define anaphora

(when ~'it

Check its truth

(do ~@body))))

Inline body

(awhen [:a :b :c] (second it)

Use it in body

;=> :b

(awhen nil (println "Will never get here"))

;=> nil

(awhen :outer (awhen :inner [it]))

Fail to nest

;=> [:inner]

9 Anaphora is pronounced un-NAF-er-uh.

Download from Wow! eBook <www.wowebook.com>

Using macros to manage resources

171

Clojure provides similar macros that do nest and replace the need for anaphora: if-

let and when-let. When designing your own macros, it’s preferred that you build

them along these lines so that the macro itself takes the name to be bound. But just

because typical anaphorics are limited, that’s not to say that they’re entirely useless.

Instead, for your own libraries you may find that their usage is intuitive. You’ll see the

pattern ~'symbol at times in Clojure macros, because this is the idiomatic way to selec-

tively capture a symbolic name within the body of a macro. The reason for this bit of

awkwardness10 is because Clojure’s syntax-quote attempts to resolve symbols in the

current context, resulting in fully qualified symbols. Therefore, ~' avoids that resolu-

tion by unquoting a quote.

8.5.2

(Arguably) useful selective name capturing

We contend that there’s only one case to be made for selective name capturing in Clo-

jure macros—the case when you’re forced to embed third-party macros and functions

in your macros that rely on the existence of anaphora. One such macro is the proxy

macro in Clojure’s core libraries, which provides an anaphoric symbol named this

within its body for use therein. We’ll cover the proxy macro in depth in section 9.1, so

there’s no need to discuss it here. But bear in mind that should this macro ever be

embedded within your own macros, you may be forced to use the ~'this pattern.

HYGIENE A hygienic macro is one that doesn’t cause name capturing at macro

expansion time. Clojure macros help to ensure hygiene by namespace-

resolving symbols within the body of syntax-quote at macro-definition time.

As you saw, symbols are expanded into the form user/a-symbol within the

body of syntax-quote. To close this hygienic loop, Clojure also disallows the

definition of qualified locals within the body of a macro. In order to selec-

tively capture names within Clojure macros, you must explicitly do so via the

~'a-symbol pattern.

Clojure prefers that symbols be either declared or bound at macro-definition time.

But using the resolution deferment strategy outlined earlier, you can relax this

requirement for those instances where doing so would be useful.

8.6

Using macros to manage resources

Managing scarce resources or those with a finite lifetime is often viewed as a sweet

spot for macro usage. In Java, such activities are almost always performed using the

try/catch/finally idiom (Bloch 2008), as shown:

try {

// open the resource

}

catch (Exception e) {

// handle any errors

10 Awkwardness is good since it’s a strong signal to make the user aware he is drifting away from the true path to clojure enlightenment. —Christophe Grand

Download from Wow! eBook <www.wowebook.com>

172

CHAPTER 8 Macros

}

finally {

// in any case, release the resource

}

We showed in section 1.5.8 that Clojure also has a try/catch/finally form that can

be used in the same way, but like the Java idiom, you must remember to explicitly

close the resource within the finally block. Clojure provides a generic with-open

macro, demonstrated in listing 8.8, that when given a “closeable” object bound to a

name, will automatically call its .close method (assuming that one exists) within a

finally block.

Listing 8.8 An example of with-open

(import [java.io BufferedReader InputStreamReader]

[java.net URL])

(defn joc-www []

(-> "http://joyofclojure.com/hello" URL.

.openStream InputStreamReader. BufferedReader.))

(let [stream (joc-www)]

(with-open [page stream]

Begin IO block

(println (.readLine page))

(print "The stream will now close... "))

Close implicitly

(println "but let's read from it anyway.")

(.readLine stream))

Use illegally after close

; Hello Cleveland

; The stream will now close... but let's read from it anyway.

; java.io.IOException: Stream closed

Not all instances of resources in your own programs will be closeable. In these

instances, we present a generic template for resource allocating macros that can be

used for many cases, shown in the following listing.

Listing 8.9 A more general template for with-open-like macros

(defmacro with-resource [binding close-fn & body]

`(let ~binding

(try

(do ~@body)

(finally

(~close-fn ~(binding 0))))))

(let [stream (joc-www)]

(with-resource [page stream]

#(.close %)

(.readLine page)))

The macro with-resource is generic enough and so generally ubiquitous across dif-

fering flavors (Symbolics Inc.11) to almost be considered a Lisp design pattern. The

11 The spirit of this section was inspired by a similar discussion of “Writing Macros to Surround Code.” If you

can get your hands on the original Symbolics manuals, do so—they contain a wealth of information.

Download from Wow! eBook <www.wowebook.com>

Putting it all together: macros returning functions

173

macro with-resource differs from with-open in that it does not assume that its

resource is closeable but instead delegates the task of closing the resource to a close-

fn function taken as an argument. One final point is that with-resource avoids the

nesting problem of anaphoric macros because it requires that the resource be named

explicitly a la [stream (joc-www)]. This approach allows for the proper nesting of

with-resource macros; and in fact, the use of named bindings marked by vectors is

ubiquitous and idiomatic in Clojure.

8.7

Putting it all together: macros returning functions

In section 7.1, we introduced Clojure’s constraint facility that uses pre- and post-

condition checks on function arguments and return values respectively to ensure

some assertions about said function. In that section, we talked briefly about how sepa-

rating the constraints from the functions they’re constraining allows you to more flex-

ibly apply different assertion templates based on need and context.

CLOJURE APHORISM Clojure programmers don’t write their apps in Clojure.

They write the language that they use to write their apps in Clojure.

In this section, we’re going to take this idea one step further by introducing a macro

named contract that implements a simple DSL to describe function constraints. For

example, a proposed DSL should be nameable and describe its pre- and post-

conditions in an intuitive way, building a higher-order function that will be used to

apply its constraints later. The following sketches a contract specifying that a function

should take only a positive number and return its value multiplied by 2:

(contract doubler

[x]

(:require

(pos? x))

(:ensure

(= (* 2 x) %)))

The contract’s :require list (Meyer 2000) refers to preconditions, and the :ensure

list the postconditions. Given this description, how would you start to implement a

macro to realize this sketch? If you haven’t already gathered from the section title and

the initial problem statement, the macro must return a function, so we’ll start there

with the following listing.

Listing 8.10 The contract top-level macro

(declare collect-bodies)

(defmacro contract [name & forms]

(list* `fn name (collect-bodies forms)))

The contract macro calls a function collect-bodies that hasn’t been written yet, so

we had to use declare to avoid a compilation error. Hold fast, because we’re going to

implement that necessary function soon. But first, imagine what the form of the

returned function will be when it finally comes out of contract:

Download from Wow! eBook <www.wowebook.com>

174

CHAPTER 8 Macros

(fn doubler

([f x]

{:post [(= (* 2 x) %)],

:pre [(pos? x)]}

(f x)))

We also want to allow for the multi-arity function definition form so that the con-

tract can take more than one specification per arity function, each separated by a

vector of symbols. The first step down that path starts with an implementation of

collect-bodies:

(declare build-contract)

(defn collect-bodies [forms]

(for [form (partition 3 forms)]

(build-contract form)))

The primary task of collect-bodies is to build a list of the body portion of the

contract, each partitioned into three segments. These partitions represent the arg-

list, requires, and ensures of the contract, which we’ll then pass along to another

function named build-contract, that will build the arity bodies and corresponding

constraint maps. This is shown next.

Listing 8.11 The contract auxiliary function build-contract

(defn build-contract [c]

(let [args (first c)]

Grab args

(list

Build list...

(into '[f] args)

Include HOF + args

(apply merge

(for [con (rest c)]

(cond (= (first con) :require)

Process requires

(assoc {} :pre (vec (rest con)))

(= (first con) :ensure)

Process ensures

(assoc {} :post (vec (rest con)))

:else (throw (Exception. (str "Unknown tag " (first con)))))))

(list* 'f args))))

Build call to f

The function build-contract is where the heart of contract construction lies, build-

ing the arity bodies that contain constraint maps. The difference is that each body is a

higher-order function that takes an additional function as an argument, which the

arguments are then delegated to. This allows us to compose the contract function

with a constrained function, as shown in the next listing.

Listing 8.12 Composition of contract function and constrained function

(def doubler-contract

Define contract

(contract doubler

[x]

(:require

(pos? x))

(:ensure

(= (* 2 x) %))))

Download from Wow! eBook <www.wowebook.com>

Putting it all together: macros returning functions

175

(def times2 (partial doubler-contract #(* 2 %)))

Test correct fn

(times2 9)

;=> 18

(def times3 (partial doubler-contract #(* 3 %)))

Test incorrect fn

(times3 9)

; java.lang.AssertionError: Assert failed: (= (* 2 x) %)

As you might expect, times2 fulfills the contract, whereas times3 doesn’t. We could

extend doubler-contract to handle extended arities, as shown here.

Listing 8.13 Contract for multiple-arity functions

(def doubler-contract

(contract doubler

[x]

Define 1-arg contract

(:require

(pos? x))

(:ensure

(= (* 2 x) %))

[x y]

Define 2-arg contract

(:require

(pos? x)

(pos? y))

(:ensure

(= (* 2 (+ x y)) %))))

((partial doubler-

contract #(* 2 (+ %1 %2))) 2 3)

Test a correct fn

;=> 10

((partial doubler-

contract #(+ %1 %1 %2 %2)) 2 3)

Test another correct fn

;=> 10

((partial doubler-contract #(* 3 (+ %1 %2))) 2 3)

Test an incorrect fn

; java.lang.AssertionError:

; Assert failed: (= (* 2 (+ x y)) %)

We could extend the contract to cover any number of expected function arities using

contract, independent of the functions themselves. This provides a nice separation

of the work to be done from the expected work to be done. By using the contract

macro, we’ve provided a way to describe the expectations of a function, including but

not limited to

 The possible types of its inputs and output

 The relationship of the function output to its inputs

 The expected function arities

 The “shape” of the inputs and output

The contract macro could be extended in many complementary ways. For example,

Clojure’s function constraints are verified using logical and—the implications being

that any additional pre- or postcondition works to tighten the requirements. But there

may be times when loosening the constraints on the inputs and tightening them on the

Download from Wow! eBook <www.wowebook.com>

176

CHAPTER 8 Macros

output makes more sense. In any case, this section isn’t about the nuances of contracts

programming, and to dig deeper would elude the point that using macros to return

functions is an extremely powerful way to extend the capabilities of Clojure itself.

8.8

Summary

We’ve explored various use cases for macros and given examples of each. Though

instructive to the point under discussion, we also tried to show how macros can be

used to mold Clojure into the language that shortens the gap between your problem

space and solution space. In your own unique programs, you should try to do the

same. But the most important skill that you can learn on your path toward macro mas-

tery is the ability to recognize when to avoid using them. The general answer of course

is whenever, and as often as you can.

In the next chapter, we’ll cover various powerful way to organize and categorize

data types and functions using Clojure’s namespaces, multimethods, types, and

protocols.

Download from Wow! eBook <www.wowebook.com>

Combining

data and code

This chapter covers

 Namespaces

 Exploring Clojure multimethods with the Universal 

Design Pattern

 Types, protocols, and records

 Putting it all together: a fluent builder for chess moves

Clojure provides powerful features for grouping and partitioning logical units of

code and data. Most logical groupings occur within namespaces, Clojure’s ana-

logue to Java packages. We explore how to build, manipulate, and reason about

them. Also, in this chapter we’ll play with Clojure’s powerful multimethods that

provide polymorphism based on arbitrary dispatch functions. We then uncover

recent additions to Clojure supporting abstraction-oriented programming —types, pro-

tocols, and records. Finally, the chapter concludes with the creation of a fluent

chess-move facility, comparing a Java approach to solving the problem with a Clo-

jure approach.

177

Download from Wow! eBook <www.wowebook.com>

178

CHAPTER 9 Combining data and code

9.1

Namespaces

Newcomers to Clojure have a propensity to hack away at namespace declarations until

they appear to work. This may work sometimes, but it delays the process of learning

how to leverage namespaces more effectively.

From a high-level perspective, namespaces can be likened to a two-level mapping,

where the first level is a symbol to a namespace containing mappings of symbols to

Vars, as shown in figure 9.1. This conceptual model1 is slightly complicated by the fact

that namespaces can be aliased, but even in these circumstances the model holds true.

In the simplest possible terms, qualified symbols of the form joy.ns/authors

cause a two-level lookup: a symbol joy.ns used to lookup a namespace map and a

symbol authors used to retrieve a Var, as shown in the following listing.

Listing 9.1 Namespace navigation

(in-ns 'joy.ns)

(def authors ["Chouser"])

(in-ns 'your.ns)

(clojure.core/refer 'joy.ns)

joy.ns/authors

;=> ["Chouser"]

(in-ns 'joy.ns)

(def authors ["Chouser" "Fogus"])

(in-ns 'your.ns)

joy.ns/authors

;=> ["Chouser" "Fogus"]

Because a symbolic name refers to a Var in the current namespace or another, it fol-

lows that any referred Var always evaluates to the current value and not the value pres-

ent at referral time.

joy.nc/authors

pre

r fix

fi e

x s

s

jo

j y

o .

y n

. s

n

:use

'authors

'joy.ns

:as

'j

:import

'Date

Figure 9.1 The logical layout of

(var authors)

namespaces. The process to resolve

a Var joy.ns/authors includes a

j/authors

symbolic resolution of the namespace

and the Var name. The result is the

Var itself. Aliases created with :use

java.util.Date

work as expected.

1 As always, we’re trying to keep the level of discussion limited to abstractions rather than implementation

details.

Download from Wow! eBook <www.wowebook.com>

Namespaces

179

9.1.1

Creating namespaces

There are a number of ways to create a new namespace; each has its advantages and

use cases. The choice of one namespace-creation mechanism over another amounts

to choosing the level of control over the default symbolic mappings.

NS

In idiomatic Clojure source code, you’ll see the ns macro used almost exclusively. By

using the ns macro, you automatically get two sets of symbolic mappings—all classes

in the java.lang package and all of the functions, macros, and special forms in the

clojure.core namespace:

(ns chimp)

(reduce + [1 2 (Integer. 3)])

;=> 6

Using the ns macro creates a namespace if it doesn’t already exist, and switches to that

namespace. The ns macro is intended for use in source code files and not in the REPL,

although there’s nothing preventing it.

IN-NS

Using the in-ns function also imports the java.lang package like ns; but it doesn’t

create any mappings for functions or macros in clojure.core. The in-ns function

also takes an explicit symbol used as the namespace qualifier, as in

(in-ns 'gibbon)

(reduce + [1 2 (Integer. 3)])

; java.lang.Exception: Unable to resolve symbol: reduce in this context

(clojure.core/refer 'clojure.core)

(reduce + [1 2 (Integer. 3)])

;=> 6

The in-ns function is more amenable to REPL experimentation when dealing with

namespaces than ns.

CREATE-NS

The finest level of control for creating namespaces is provided through the create-ns

function, which when called takes a symbol and returns a namespace object:

(def b (create-ns 'bonobo))

b

;=> #<Namespace bonobo>

((ns-map b) 'String)

;=> java.lang.String

The call to create-ns doesn’t switch to the named namespace, but it does create Java

class mappings automatically. When given a namespace object (retrieved using the

find-ns function also), you can manipulate its bindings programmatically using the

functions intern and ns-unmap:

Download from Wow! eBook <www.wowebook.com>

180

CHAPTER 9 Combining data and code

(intern b 'x 9)

;=> #'bonobo/x

bonobo/x

;=> 9

In the preceding code, we bound the symbol x to the value 9 in the namespace

bonobo, and then referenced it directly using its qualified name bonobo/x. We can do

the same thing for any type of Var binding:

(intern b 'reduce clojure.core/reduce)

;=> #'bonobo/reduce

(intern b '+ clojure.core/+)

;=> #'bonobo/+

(in-ns 'bonobo)

(reduce + [1 2 3 4 5])

;=> 15

Because only Java class mappings are created by create-ns, you’ll have to intern any

Clojure core functions, as we did with + and reduce. You can even inspect the map-

pings within a namespace programmatically, and likewise remove specific mappings:

(in-ns 'user)

(get (ns-map 'bonobo) 'reduce)

;=> #'bonobo/reduce

(ns-unmap 'bonobo 'reduce) ;=> nil

(get (ns-map 'bonobo) 'reduce)

;=> nil

Finally, you can wipe a namespace using remove-ns:

(remove-ns 'bonobo)

;=> #<Namespace bonobo>

(all-ns)

;=> (#<Namespace clojure.set> #<Namespace clojure.main>

#<Namespace clojure.core> #<Namespace clojure.zip>

#<Namespace chimp> #<Namespace gibbon>

#<Namespace clojure.xml>)

You should be careful when populating namespaces

using create-ns and intern, because they cause

potentially confusing side-effects to occur. Their use is

intended only for advanced techniques, and even then

they should be used cautiously.

9.1.2

Expose only what’s needed

Knowing that namespaces operate as a two-level map-

ping will only get you so far in creating and using them

effectively. You must understand other practical matters

to use namespaces to their fullest. For example, for a

Figure 9.2 Namespace private

directories: the directories layout

given namespace joy.contracts, your directory struc-

for an illustrative joy.contracts

ture could look like that in figure 9.2.

namespace

Download from Wow! eBook <www.wowebook.com>

Namespaces

181

This directory structure is fairly

straightforward, but there are a couple

items to note. First, though the

namespace is named joy.contracts,

the corresponding Clojure source file is

located in the contracts-lib/src/joy

directory. This is a common technique

in organizing Java and Clojure projects,

where the actual source directories and

files are located in a common src subdi-

Figure 9.3 Namespace private source: the top of

rectory in the main project directory.

the source file for the joy.contracts namespace

The additional files build.xml, pom.xml,

and project.clj correspond to the build scripts for Apache Ant, Maven, and Leiningen,

respectively. These build scripts will know, through either configuration or conven-

tion, that the src directory contains the directories and source files for Clojure

namespaces and not part of the namespace logical layout. If you were to open the con-

tracts.clj file located in contracts-lib/src/joy in your favorite editor, then you might

see something like that shown in figure 9.3.

The file contracts.clj defines the namespace joy.contracts and defines the func-

tion build-contract using the defn- macro. The use of defn- in this way indicates to

Clojure that the build-contract function is private to the joy.contracts

namespace. The defn- macro is provided for convenience and simply attaches privi-

leged metadata to the Var containing the function. You could attach the same

namespace privacy metadata yourself, as shown:

(ns hider.ns)

(defn ^{:private true} answer [] 42)

(ns seeker.ns

(:refer hider.ns))

(answer)

; java.lang.Exception: Unable to resolve symbol: answer in this context

The use of ^{:private true} in this way will also work within a def and a defmacro,

and for these cases it’s required, because there’s no corresponding def- and def-

macro- in Clojure’s core.

HYPHENS/UNDERSCORES If you decide to name your namespaces with

hyphens, à la my-cool-lib, then the corresponding source file must be

named with underscores in place of the hyphens (my_cool_lib.clj).

Because Clojure namespace names are tied to the directory in which they reside, you

can also create a certain directory structure conducive to hiding implementation

details, as seen in figure 9.4.

By creating another subdirectory to contracts-lib/src/joy named impl, you can

effectively hide implementation details for your code. The public-facing API would be

Download from Wow! eBook <www.wowebook.com>

182

CHAPTER 9 Combining data and code

located in contracts.clj and the “hidden” implemen-

tation details in impl.clj. Your clients would be

expected to refer only to the elements in con-

tracts.clj, whereas your library could refer to ele-

ments in impl.clj, as shown in figure 9.5.

Of course, nothing’s stopping you from also ref-

erencing the joy.contracts.impl namespace, but

you do so at their own peril. There are never any

guarantees that implementation details will remain

Figure 9.4 Private API directories:

the same shape from one release to the next.

using the folder layout to hide

namespace implementation details

9.1.3

Declarative inclusions and exclusions

When defining namespaces, it’s important to include only the references that are

likely to be used. Clojure prefers a fine-grained Var mapping via a set of directives on

the ns macro: :exclude, :only, :as, :refer-clojure, :import, :use, :load, and

:require.

We’ll describe a namespace named joy.ns-ex first in prose and then using ns and

its directives. In this namespace, we want to exclude the defstruct macro from

Figure 9.5 Private API source: the client-facing API is located in

contracts.clj and the private API in impl.clj.

Download from Wow! eBook <www.wowebook.com>

Exploring Clojure multimethods with the Universal Design Pattern

183

clojure.core. Next, we want to use everything in clojure.set and clojure.xml

without namespace qualification. Likewise, we wish to use only the functions are and

is from the clojure.test namespace without qualification. We then want to load the

clojure.zip namespace and alias it as z. Finally, we want to import the Java classes

java.util.Date and java.io.File. By providing directives, the problem of

namespace inclusions and exclusions become a declarative matter, as shown:

(ns joy.ns-ex

(:refer-clojure :exclude [defstruct])

(:use (clojure set xml))

(:use [clojure.test :only (are is)])

(:require (clojure [zip :as z]))

(:import (java.util Date)

(java.io File)))

We’ll touch on further uses of namespaces throughout the rest of the book, with an

extensive example explaining their use as JVM class specifications in section 10.3.

AVOID NAKED :USE One point of note that we should mention is that the

(:use (clojure set xml)) statement is considered a promiscuous operation

and therefore discouraged. The :use directive without the :only option pulls

in all of the public Vars in clojure.set and clojure.xml indiscriminately.

Though this practice is useful when incrementally building your code, it

shouldn’t endure into the production environment. When organizing your

code along namespaces, it’s good practice to export and import only those

elements needed.

We now turn our focus to Clojure’s multimethods, a way of defining polymorphic

functions based on the results of arbitrary functions, which will get you halfway toward

a system of polymorphic types.

9.2

Exploring Clojure multimethods

with the Universal Design Pattern

The most specific event can serve as a general example of a class of events.

—Douglas R. Hofstadter

In Douglas Hofstadter’s Pulitzer prize winning work Gödel, Escher, Bach: An Eternal

Golden Braid, he describes a notion of the Prototype Principle —the tendency of the

human mind to use specific events as models for similar but different events or things.

He presents the idea “that there is generality in the specific” (Hofstadter 1979). Build-

ing on this idea, programmer Steve Yegge coined the term The Universal Design Pattern

(UDP), extrapolating on Hofstadter’s idea (Yegge 2008) and presenting it in terms of

prototypal inheritance (Ungar 1987).

The UDP is built on the notion of a map or map-like object. Though not ground-

breaking, the flexibility in the UDP derives from the fact that each map contains a

reference to a prototype map used as a parent link to inherited fields. You might won-

der how anyone could model a software problem in this way, but we assure you that

Download from Wow! eBook <www.wowebook.com>

184

CHAPTER 9 Combining data and code

countless programmers do so every day when they choose JavaScript (Flanagan 2006).

In this section, we’ll implement a subset of Yegge’s UDP and discuss how it might be

used as the basis for abstraction-oriented programming and polymorphism using Clo-

jure’s multimethods and ad hoc hierarchies.

9.2.1

The parts

In addition to the aforementioned prototype reference, the UDP requires a set of sup-

porting functions to operate: beget, get, put, has?, and forget. The entire UDP is

built on these five functions, but we’ll need the first three for this section.

BEGET

The beget function performs a simple task. It takes a map and associates its prototype

reference to another map, returning a new map:

(ns joy.udp

(:refer-clojure :exclude [get]))

(defn beget [o p] (assoc o ::prototype p))

(beget {:sub 0} {:super 1})

;=> {:joy.udp/prototype {:super 1}, :sub 0}

To participate in the UDP, maps must have a :joy.udp/prototype entry.

PUT

The function put takes a key and an associated value and puts them into the supplied

map, overwriting any existing key of the same name:

(def put assoc)

The put function is asymmetric to the functionality of get. The get method retrieves

values anywhere along the prototype chain, whereas put only ever inserts at the level

of the supplied map.

GET

Because of the presence of the prototype link, get requires more than a simple one-

level lookup. Instead, whenever a value isn’t found in a given map, the prototype

chain is followed until the end:

(defn get [m k]

(when m

(if-let [[_ v] (find m k)]

v

(recur (::prototype m) k))))

(get (beget {:sub 0} {:super 1})

:super)

;=> 1

We don’t explicitly handle the case of “removed ”properties, but instead treat them

like any other associated value. This is fine because the “not found” value of nil is

falsey. Most of the time, it’s sufficient to rely on the fact that looking up a nonexistent

Download from Wow! eBook <www.wowebook.com>

Exploring Clojure multimethods with the Universal Design Pattern

185

key will return nil. But in cases where you want to allow users of your functions to

store any value at all, including nil, you’ll have to be careful to distinguish nil from

“not found,” and the find function is the best way to do this.

9.2.2

Usage

Using only beget, put, and get, you can leverage the UDP in some simple, yet power-

ful ways. Assume that at birth cats like dogs and only learn to despise them when

goaded. Morris the cat has spent most of his life liking 9-Lives cat food and dogs, until

the day comes when a surly Shih Tzu leaves him battered and bruised. We can model

this unfortunate story as shown:

(def cat {:likes-dogs true, :ocd-bathing true})

(def morris (beget {:likes-9lives true} cat))

(def post-traumatic-morris (beget {:likes-dogs nil} morris))

(get cat :likes-dogs)

;=> true

(get morris :likes-dogs)

;=> true

(get post-traumatic-morris :likes-dogs)

;=> nil

The map post-traumatic-morris is like the old morris in every way except for the

fact that he has learned to hate dogs. Modeling cat and dog societal woes is interesting

but far from the only use case for the UDP, as you’ll see next.

NO NOTION OF SELF

Our implementation of the UDP contains no notion of self-awareness via an implicit

this or self reference. Though adding such a feature would probably be possible,

we’ve intentionally excluded it in order to draw a clear separation between the proto-

types and the functions that work on them (Keene 1989). A better solution, and one

that follows in line with a deeper Clojure philosophy, would be to access, use, and

manipulate these prototypes using Clojure’s multimethods.

9.2.3

Multimethods to the rescue

Adding behaviors to the UDP can be accomplished easily using Clojure’s multimethod

facilities. Multimethods provide a way to perform function polymorphism based on the

result of an arbitrary dispatch function. Coupled with our earlier UDP implementa-

tion, we can implement a prototypal object system with differential inheritance similar

to (although not as elegant as) that in the Io language (Dekorte Io). First, we’ll need

to define a multimethod compiler that dispatches on a key :os:

(defmulti compiler :os)

(defmethod compiler ::unix [m] (get m :c-compiler))

(defmethod compiler ::osx [m] (get m :c-compiler))

Download from Wow! eBook <www.wowebook.com>

186

CHAPTER 9 Combining data and code

The multimethod compiler describes a simple scenario: if the function compiler is

called with a prototype map, then the map is queried for an element :os, which has

methods defined on the results for either ::unix or ::osx. We’ll create some proto-

type maps to exercise compiler:

(def clone (partial beget {}))

(def unix {:os ::unix, :c-compiler "cc", :home "/home", :dev "/dev"})

(def osx (-> (clone unix)

(put :os ::osx)

(put :c-compiler "gcc")

(put :home "/Users")))

(compiler unix)

;=> "cc"

(compiler osx)

;=> "gcc"

That’s all there is (Foote 2003) to creating behaviors that work against the specific

“type” of a prototype map. But a problem of inherited behaviors still persists. Because

our implementation of the UDP separates state from behavior, there’s seemingly no

way to associate inherited behaviors. But as we’ll now show, Clojure does provide a way

to define ad hoc hierarchies that we can use to simulate inheritance within our model.

9.2.4

Ad hoc hierarchies for inherited behaviors

Based on the layout of the unix and osx prototype maps, the property :home is over-

ridden in osx. We could again duplicate the use of get within each method defined

(as in compiler), but instead we prefer to say that the lookup of :home should be a

derived function:

(defmulti home :os)

(defmethod home ::unix [m] (get m :home))

(home unix)

;=> "/home"

(home osx)

; java.lang.IllegalArgumentException:

; No method in multimethod 'home' for dispatch value: :user/osx

Clojure allows you to define a relationship stating “::osx is a ::unix” and have the

derived function take over the lookup behavior using Clojure’s derive function:

(derive ::osx ::unix)

Now the home function works:

(home osx)

;=> "/Users"

You can query the derivation hierarchy using the functions parents, ancestors,

descendants, and isa? as shown:

(parents ::osx)

;=> #{:user/unix}

Download from Wow! eBook <www.wowebook.com>

Exploring Clojure multimethods with the Universal Design Pattern

187

(ancestors ::osx)

;=> #{:user/unix}

(descendants ::unix)

;=> #{:user/osx}

(isa? ::osx ::unix)

;=> true

(isa? ::unix ::osx)

;=> false

The result of the isa? function defines how multimethods dispatch. In the absence of

a derivation hierarchy, isa? can be likened to pure equality, but with it traverses a der-

ivation graph.

9.2.5

Resolving conflict in hierarchies

What if we interject another ancestor into the hierarchy for ::osx and want to again

call the home method? Observe the following:

(derive ::osx ::bsd)

(defmethod home ::bsd [m] "/home")

(home osx)

; java.lang.IllegalArgumentException: Multiple methods in multimethod

; 'home' match dispatch value: :user/osx -> :user/unix and

; :user/bsd, and neither is preferred

As shown in figure 9.6, ::osx derives from both ::bsd and ::unix, so there’s no way

to decide which method to dispatch, because they’re both at the same level in the der-

ivation hierarchy. Fortunately, Clojure provides a way to assign favor to one method

over another using the function prefer-method:

(prefer-method home ::unix ::bsd)

(home osx)

;=> "/Users"

In this case, we used prefer-method to explicitly state that for the multimethod home,

we prefer the method associated with the dispatch value ::unix over the one for

::bsd, as illustrated in figure 9.5. As you’ll recall, the home method for ::unix explic-

itly used get to traverse the prototype chain, which is the preferred behavior.

As you might expect, removing the home method for the ::bsd dispatch value

using remove-method will remove the preferential lookup for ::osx:

(remove-method home ::bsd)

(derive ::osx ::unix)

(home osx)

::unix

;=> "/Users"

::osx

All of these functions manipulate and

Figure 9.6 Hierarchy conflict:

most languages allowing type

operate off of the global hierarchy map

(derive ::osx ::bsd)

derivations use a built-in

directly. If you prefer to reduce these

::unix

::bsd

conflict-resolution strategy. In

potentially confusing side-effects, then

the case of CLOS, it’s fully

::osx

you can define a derivation hierarchy

customizable. Clojure requires

(home osx)

conflicts to be resolved with

using make-hierarchy and derive:

???

prefer-method.

Download from Wow! eBook <www.wowebook.com>

188

CHAPTER 9 Combining data and code

(derive (make-hierarchy) ::osx ::unix)

;=> {:parents {:user/osx #{:user/unix}},

:ancestors {:user/osx #{:user/unix}},

:descendants {:user/unix #{:user/osx}}}

Once you have a separate hierarchy in hand, you can provide it to defmulti to specify

the derivation context, thus preserving the global hierarchy map.

9.2.6

Arbitrary dispatch for true maximum power

Until now, we’ve only exercised multimethods using a single privileged :os property,

but this doesn’t accentuate their true power. Instead, multimethods are fully open and

can dispatch on the result of an arbitrary function, even one that can pull apart and/

or combine its inputs into any form:

(defmulti compile-cmd (juxt :os compiler))

(defmethod compile-cmd [::osx "gcc"] [m]

(str "/usr/bin/" (get m :c-compiler)))

(defmethod compile-cmd :default [m]

(str "Unsure where to locate " (get m :c-compiler)))

The dispatch values for the new compile-cmd methods are vectors composed of the

results of looking up the :os key and calling the compiler function defined earlier.

You can now observe what happens when compile-cmd is called:

(compile-cmd osx)

;=> "/usr/bin/gcc"

(compile-cmd unix)

;=> "Unsure where to locate cc"

Using multimethods and the UDP is an interesting way to build abstractions. Multi-

methods and ad hoc hierarchies are open systems, allowing for polymorphic dispatch

based on arbitrary functions. Clojure also provides a simpler model for creating

abstractions and gaining the benefits of polymorphism—types, protocols, and

records—which we’ll cover next.

The handy-dandy juxt function

The juxt function is highly useful in defining multimethod dispatch functions. In a

nutshell, juxt takes a bunch of functions and composes them into a function return-

ing a vector of its argument(s) applied to each given function, as shown:

(def each-math (juxt + * - /))

(each-math 2 3)

;=> [5 6 -1 2/3]

((juxt take drop) 3 (range 9))

[(0 1 2) (3 4 5 6 7 8)]

Having a convenient and succinct way to build vectors of applied functions is powerful

for defining understandable multimethods—although that’s not the limit of juxt’s

usefulness.

Download from Wow! eBook <www.wowebook.com>

Types, protocols, and records

189

9.3

Types, protocols, and records

We showed in the previous section that Clojure multimethods provide a way to

achieve runtime polymorphism based on arbitrary dispatch functions. Though

extremely powerful, multimethods are sometimes less than ideal. Interposing a dis-

patch function into the polymorphism machinery isn’t always conducive to raw speed.

Likewise, dispatching on an arbitrary function is often overkill. Therefore, Clojure

provides facilities for creating logically grouped polymorphic functions that are both

simple and performant—types, records, and protocols. We’ll delve into these topics in

this section and introduce the concept of abstraction-oriented programming, predi-

cated on the creation of logical groupings. But first, we’ll discuss the simplest of the

three topics, records, which you might recognize.

9.3.1

Records

Using maps as data objects is perfectly acceptable and has several lovely features.

Chief among these is that maps require no declaration of any sort: you just use literal

syntax to build them right on the spot. We showed this in section 7.2 when we built an

object like this:

{:val 5, :l nil, :r nil}

This is handy but is missing things that are often desirable, the most significant of

which is a type of its own. The object constructed here is some kind of map, but it

isn’t, as far as Clojure is concerned, a TreeNode. That means that when used in its sim-

ple form as we did here, there’s no clean way2 to determine whether any particular

map is a TreeNode or not.

In such circumstances, records become a compelling3 solution. You define a

record type with a defrecord form. For example, a defrecord for TreeNode looks like

this:

(defrecord TreeNode [val l r])

This creates a new Java class with a constructor that takes a value for each of the fields

listed. It also imports that class into your current namespace so you can easily use it to

create new instances.

Here’s how to create an instance of the TreeNode record:

(TreeNode. 5 nil nil)

;=> #:user.TreeNode{:val 5, :l nil, :r nil}

2

You could test a map for the existence of the keys :val, :l, and :r, a sort of duck-typing but on fields instead

of methods. But because there exists a real possibility than some other kind of object may happen to have

these keys but use them in a different way, undesirable complexity and/or unexpected behavior is likely. For-

tunately, you can mitigate this risk by using namespace-qualified keywords. Despite the general agreement of

experts that ducks are Kosher, we’d definitely classify this particular duck as unclean.

3

There was a pre-Clojure 1.2 convention of attaching :type metadata to an object, which can be looked up

with the type function, but this approach is rarely if ever needed moving forward.

Download from Wow! eBook <www.wowebook.com>

190

CHAPTER 9 Combining data and code

Explicit importing of defrecord and deftype classes

It’s important to note that when you define a defrecord and deftype, corresponding

classes are generated. These classes are automatically imported into the same

namespace where the defrecord and deftype declarations occur, but not in any

other namespace. Instead, you must explicitly import defrecord and deftype

classes using the import function or :import namespace declaration:

(ns my-cool-ns

(:import joy.udp.TreeNode))

Loading a namespace via :require or :use won’t be enough to import defrecord

and deftype classes.

The use of defrecord buys you several important benefits. First of all, it provides a

simple and specific idiom for documenting the expected fields of the object. But it

also delivers several important performance improvements. A record will be created

more quickly, consume less memory, and look up keys in itself more quickly than the

equivalent array map or hash map. Data types can also store primitive values (byte, int,

long, and so on), which take up considerably less memory than the equivalent boxed

objects (Byte, Integer, Long, and so on) supported by other collection types.

That’s a lot of benefit, so what does it cost you? The first cost we already men-

tioned—you must define the record type before using it. Another is that currently,

records aren’t printed in a way that the Clojure reader can read, unlike hash maps.

This can be a problem if you’re using Clojure’s print functions to save off or transmit

data. Here’s what it looks like if we try, successfully, with a literal map and then again,

unsuccessfully, with a record:

The downfall of defstructs

Clojure provides a defstruct mechanism, which can be viewed as a way to define a

map that acts as an ad hoc class mechanism. These structs defined a set of keys

that were required to exist in the map and could therefore not be removed via dissoc.

With the advent of defrecord, the need for structs has been nearly eliminated, and

therefore structs aren’t covered in this book. But if you have a code base reliant on

structs, then a record can replace them with minimal code changes, as highlighted

here:

(defn change-age [p] (assoc p :age 286))

(defstruct person :fname :lname)

(change-age (struct person "Immanuel" "Kant"))

;=> {:fname "Immanuel", :lname "Kant", :age 286}

(defrecord Person [fname lname])

(change-age (Person. "Immanuel" "Kant"))

;=> #:user.Person{:fname "Immanuel", :lname "Kant", :age 286}

Note that the change-age function works with either structs or records—no change

is required. Only the definition and the mechanism of instantiation need to be

updated.

Download from Wow! eBook <www.wowebook.com>

Types, protocols, and records

191

(read-string (pr-str {:val 5, :l nil, :r nil}))

;=> {:val 5, :l nil, :r nil}

(read-string (pr-str (TreeNode. 5 nil nil)))

; java.lang.RuntimeException: java.lang.Exception: No dispatch macro for:

This may change eventually, but there are some tricky problems yet to be worked out

before records can be printed and read back in.

Other noteworthy differences between maps and records include

 Records, unlike maps, can’t serve as functions.

 Records are never equal to maps with the same key/value mappings.

You still look up values in records by doing (:keyword obj); it’s just that if obj is a

record, this code will run dramatically faster. By the way, that means destructuring will

still work as well. Records support metadata using with-meta and meta just like other

Clojure collections, and you can even redefine a record if desired to have different

fields giving you the compiled performance of Java dynamically. All of these together

mean you can build a lot of code on top of simple hash-map objects and then make

minimal changes to switch to using records instead, gaining all the performance ben-

efits we already covered.

You should understand records well enough to be able to reimplement the persis-

tent binary tree from chapter 5 using defrecord instead of maps. This is shown in the

following listing. Note that we had to add the defrecord and change the expressions

in xconj where objects are created, but the xseq function is defined identically to how

it was before.

Listing 9.2 Persistent binary tree built of records

(defrecord TreeNode [val l r])

Define record type

(defn xconj [t v]

Add to tree

(cond

(nil? t) (TreeNode. v nil nil)

(< v (:val t)) (TreeNode. (:val t) (xconj (:l t) v) (:r t))

:else (TreeNode. (:val t) (:l t) (xconj (:r t) v))))

(defn xseq [t]

Convert trees

(when t

to seqs

(concat (xseq (:l t)) [(:val t)] (xseq (:r t)))))

(def sample-tree (reduce xconj nil [3 5 2 4 6]))

Try it all out

(xseq sample-tree)

;=> (2 3 4 5 6)

You can assoc and dissoc any key you want—adding keys that weren’t defined in the

defrecord works, though they have the performance of a regular map. Perhaps more

surprisingly, dissocing a key given in the record works but returns a regular map

rather than a record. In this example, note that the return value is printed as a plain

map, not with the #:user.TreeNode prefix of a record:

Download from Wow! eBook <www.wowebook.com>

192

CHAPTER 9 Combining data and code

(dissoc (TreeNodePlus 5 nil nil) :l)

;=> {:val 5, :r nil}

A final benefit of records is how well they integrate with Clojure protocols. But to fully

understand how they relate, we must first explore what protocols are.

9.3.2

Protocols

The establishment of protocols ... creates an obvious way for two people who are

not directly communicating to structure independently developed code so that it

works in a manner that remains coherent when such code is later combined.

—Kent M. Pitman (Pitman 2001)

A protocol in Clojure is simply a set of function signatures, each with at least one param-

eter, that are given a collective name. They fulfill a role somewhat like Java interfaces

or C++ pure virtual classes—a class that claims to implement a particular protocol

should provide specialized implementations of each of the functions in that protocol.

Then, when any of those functions is called, the appropriate implementation is poly-

morphic on the type of the first parameter, just like Java. In fact, the first parameter to

a protocol function corresponds to the target object (the thing to the left of the dot

for a method call used in Java source) of a method in object-oriented parlance.

For example, consider what collections such as stacks (First In, Last Out: FILO)

and queues (First In, First Out: FIFO) have in common. Each has a simple function for

inserting a thing (call it push), a simple function for removing a thing (pop), and usu-

ally a function to see what would be removed if you removed a thing (peek). What we

just gave you was an informal description of a protocol; all that’s missing is the name.

We can replace the changing third item of the acronym with an X and call objects that

provide these functions FIXO. Note that besides stacks and queues, FIXO could

include priority queues, pipes, and other critters.

So now let’s look at that informal description rewritten as a formal Clojure

definition:

(defprotocol FIXO

(fixo-push [fixo value])

(fixo-pop [fixo])

(fixo-peek [fixo]))

...and that’s it. The only reason we prefixed the function names with fixo- is so that

they don’t conflict with Clojure’s built-in functions.4 Besides that, it’s hard to imagine

how there could be much less ceremony, isn’t it?

But in order for a protocol to do any good, something must implement it. Proto-

cols are implemented using one of the extend forms: extend, extend-type,5 or

extend-protocol. Each of these does essentially the same thing, but extend-type and

4

It would be better to fix this problem by defining FIXO in a new namespace and excluding from it the simi-

larly named clojure.core functions, except this would be a distraction from the point of this section. We’ll dis-

cuss interesting interactions between namespaces and protocols later in this chapter.

5

Records are a specialized kind of data type, so extend-type is used for both. We’ll look at data types later

in this section.

Download from Wow! eBook <www.wowebook.com>

Types, protocols, and records

193

extend-protocol are convenience macros for when you want to provide multiple

functions for a given type. For example, the binary TreeNode from listing 9.2 is a

record, so if we want to extend it, extend-type would be most convenient. Because

TreeNode already has a function xconj that works just like push should, we’ll start by

implementing that:

(extend-type TreeNode

FIXO

(fixo-push [node value]

(xconj node value)))

(xseq (fixo-push sample-tree 5/2))

;=> (2 5/2 3 4 5 6)

The first argument to extend-type is the class or interface that the entire rest of the

form will be extending. Following the type name are one or more blocks, each starting

with the name of the protocol to be extended and followed by one or more functions

from that protocol to implement. So in the preceding example, we’re implementing a

single function fixo-push for TreeNode objects, and we call the existing xconj func-

tion. Got it? The reason this is better than simply defining a regular function named

fixo-push is that protocols allow for polymorphism. That same function can have a

different implementation for a different kind of object. Clojure vectors can act like

stacks by extending FIXO to vectors:

(extend-type clojure.lang.IPersistentVector

FIXO

(fixo-push [vector value]

(conj vector value)))

(fixo-push [2 3 4 5 6] 5/2)

;=> [2 3 4 5 6 5/2]

Here we’re extending FIXO to an interface instead of a concrete class. This means that

fixo-push is now defined for all classes that inherit from IPersistentVector. Note

that we can now call fixo-push with either a vector or a TreeNode, and the appropri-

ate function implementation is invoked.

Clojure-style mixins

As you proceed through this section, you’ll notice that we extend the FIXO protocol’s

fixo-push function in isolation. This works fine for our purposes, but you might want

to take note of the implications of this approach. Consider the following:

(use 'clojure.string)

(defprotocol StringOps (rev [s]) (upp [s]))

(extend-type String

StringOps

(rev [s] (clojure.string/reverse s)))

(rev "Works")

;=> "skroW"

Download from Wow! eBook <www.wowebook.com>

194

CHAPTER 9 Combining data and code

(continued)

Defining the StringOps protocol and extending its rev function to String seems to

work fine. But observe what happens when the protocol is again extended to cover

the remaining upp function:

(extend-type String

StringOps

(upp [s] (clojure.string/upper-case s)))

(upp "Works")

;=> "WORKS"

(rev "Works?")

; IllegalArgumentException No implementation of method: :rev

; of protocol: #'user/StringOps found for

; class: java.lang.String

The reason for this exception is that for a protocol to be fully populated (all of its func-

tions callable), it must be extended fully, per individual type. Protocol extension is at

the granularity of the entire protocol and not at a per-function basis. This behavior

seems antithetical to the common notion of a mixin—granules of discrete functional-

ity that can be “mixed into” existing classes, modules, and so on. Clojure too has

mixins, but it takes a slightly different approach:

(def rev-mixin {:rev clojure.string/reverse})

(def upp-mixin {:upp (fn [this] (.toUpperCase this))})

(def fully-mixed (merge upp-mixin rev-mixin))

(extend String StringOps fully-mixed)

(-> "Works" upp rev)

;=> SKROW

Mixins in Clojure refer to the creation of discrete maps containing protocol function

implementations that are combined in such a way as to create a complete implemen-

tation of a protocol. Once mixed together (as in the Var fully-mixed), only then are

types extended to protocols. As with many of Clojure’s features, mixins and protocol

extension are fully open.

What we’ve just done is impossible with Java interfaces or C++ classes, at least in the

order we did it. With either of those languages, the concrete type (such as TreeNode

or vector) must name at the time it’s defined all the interfaces or classes it’s going to

implement. Here we went the other way around—both TreeNode and vectors were

defined before the FIXO protocol even existed, and we easily extended FIXO to each of

them. This matters in the real world because the concrete types and even the protocol

could be provided by third-party libraries—possibly even different third-party librar-

ies—and we could still match them up, provide implementations for the appropriate

functions, and get back to work. All this without any adapters, wrappers, monkey-

patching, or other incidental complexity getting in the way. In fact, Clojure polymor-

phism lives in the protocol functions, not in the classes, as shown in figure 9.7.

Download from Wow! eBook <www.wowebook.com>

Types, protocols, and records

195

Monkey Patching

Wrapping

Pr

P o

r to

t co

c ls

TreeNode

TreeNodeWrapper

FIXO

fixo-pop

(fixo-pop TreeNode)

fixo-pop

fixo-push

(fixo-pop Vector)

fixo-push

fixo-peek

(fixo-push TreeNode)

(fixo-push Vector)

TreeNode

fixo-peek

(fixo-peek TreeNode)

(fixo-peek Vector)

Figure 9.7 As opposed to the notion of monkey-patching and wrapping,

the polymorphism in Clojure resides in the functions themselves and not

in the classes worked with.

You can even extend a protocol to nil itself. You’d be forgiven for not immediately

seeing why you’d want to do this; but consider how TreeNode implements fixo-push,

and yet the sample-tree we’re using was built using xconj instead. Trying to build up

a tree the same way with fixo-push runs into a problem:

(reduce fixo-push nil [3 5 2 4 6 0])

; java.lang.IllegalArgumentException:

; No implementation of method: :fixo-push

; of protocol: #'user/FIXO found for class: nil

The xconj implementation specifically handled the initial nil case, but because pro-

tocol methods dispatch on the first argument, we need special support from extend to

get fixo-push to behave similarly. This is done by extending a protocol to the value

nil, like this:

(extend-type nil

FIXO

(fixo-push [t v]

(TreeNode. v nil nil)))

(xseq (reduce fixo-push nil [3 5 2 4 6 0]))

;=> (0 2 3 4 5 6)

All the options and arrangements of code allowed by extend can be disorienting, but

one thing you can keep firmly in mind is that extend is always about a protocol. Each

method listed in an extend form is implementing an intersection between a protocol

and something else. That something else can be a concrete class, an interface, a

record type, or even nil, but it’s always being connected to a protocol.

See the following listing for complete implementations of FIXO for TreeNode and

vectors. As mentioned in the sidebar, in order for the FIXO protocol to be fully realiz-

able, each of its functions should be mixed in. But you might not always require that a

protocol be fully realizable.

Download from Wow! eBook <www.wowebook.com>

196

CHAPTER 9 Combining data and code

Listing 9.3 Complete implementations of FIXO for TreeNode and vector

(extend-type TreeNode

FIXO

Delegate

(fixo-push [node value]

to xconj

(xconj node value))

(fixo-peek [node]

Walk down left nodes

(if (:l node)

to find smallest

(recur (:l node))

(:val node)))

Build new path down

(fixo-pop [node]

left to removed item

(if (:l node)

(TreeNode. (:val node) (fixo-pop (:l node)) (:r node))

(:r node))))

(extend-type clojure.lang.IPersistentVector

FIXO

fixo-push is

(fixo-push [vector value]

vector's conj

(conj vector value))

(fixo-peek [vector]

peek is peek

(peek vector))

(fixo-pop [vector]

pop is pop

(pop vector)))

If you’ve done six impossible things this morning, why not round it off with breakfast

at Milliways, the Restaurant at the End of the Universe?

—Douglas Adams

Each of the function bodies in the previous example have either had no code in com-

mon with each other, or called out to another function such as xconj for implementa-

tion details that they have in common. These techniques work well when there’s a low

level of commonality between the methods being implemented, but sometimes you

have many methods of a protocol or even whole protocol implementations that you

want to extend to multiple classes. In these cases, some languages would encourage

you to create a base class that implements some or all of the methods and then inherit

from that. Clojure has a different approach.

SHARING METHOD IMPLEMENTATIONS

Clojure doesn’t encourage implementation inheritance, so although it’s possible to

inherit from concrete classes as needed for Java interoperability,6 there’s no way to use

extend to provide a concrete implementation and then build another class on top of

that. There are important reasons why Clojure intentionally avoids this, but regardless

of the reasons, we’re left with the question of how best to avoid repeating code when

similar objects implement the same protocol method.

The simplest solution is to write a regular function that builds on the protocol’s

methods. For example, Clojure’s own into takes a collection and uses the conj imple-

mentation provided by the collection. We can write a similar function for FIXO objects

like this:

6 Mechanisms that support something like Java-style implementation inheritance include gen-class, proxy,

and extending protocol methods to Java abstract classes and interfaces.

Download from Wow! eBook <www.wowebook.com>

Types, protocols, and records

197

(defn fixo-into [c1 c2]

(reduce fixo-push c1 c2))

(xseq (fixo-into (TreeNode. 5 nil nil) [2 4 6 7]))

;=> (2 4 5 6 7)

(seq (fixo-into [5] [2 4 6 7]))

;=> (5 2 4 6 7)

But this is only an option when your function can be defined entirely in terms of the

protocol’s methods. If this isn’t the case, you may need the more nuanced solution

provided by the extend function. We mentioned it earlier but so far have only given

examples of a macro built on top of it, extend-type. Though this and extend-

protocol are frequently the most convenient way to implement protocol methods,

they don’t provide a natural way to mix in method implementations. The extend func-

tion takes a map for each protocol you want to implement, and you can build that

map however you’d like, including by merging in implementations that are already

defined. In the following listing, you should note how a FIXO implementation could

be defined early using a map and extended to a protocol/record type later (while still

maintaining every benefit of using the original map).

Listing 9.4 Using a map to extend FIXO to TreeNode

(def tree-node-fixo

Define map of names

{:fixo-push (fn [node value]

to functions

(xconj node value))

:fixo-peek (fn [node]

(if (:l node)

(recur (:l node))

(:val node)))

:fixo-pop (fn [node]

(if (:l node)

(TreeNode. (:val node) (fixo-pop (:l node)) (:r node))

(:r node)))})

Extend protocol

(extend TreeNode FIXO tree-node-fixo)

using map

(xseq (fixo-into (TreeNode. 5 nil nil) [2 4 6 7]))

;=> (2 4 5 6 7)

These record objects and the way protocols can be extended to them result in rather

differently shaped code than the objects built out of closures that we showed in sec-

tion 7.2. Often this ability to define the data and implementation separately is desir-

able, but you’re likely to find yourself occasionally in a circumstance where closures

may feel like a better fit than records, and yet you want to extend a protocol or inter-

face, not just provide ad hoc method names as in section 7.2.

REIFY

The reify macro brings together all the power of function closures and all the perfor-

mance and protocol participation of extend into a single form. For example, say you

want a stack-like FIXO that’s constrained to a certain fixed size. Any attempt to push

items onto one of these fixed-fixos when it’s already full will fail, and an unchanged

Download from Wow! eBook <www.wowebook.com>

198

CHAPTER 9 Combining data and code

object will be returned. The wrinkle in the requirements that makes reify a reason-

able option is that you’ll want this size limit to be configurable. Thus you’ll need a

constructor or factory function, shown next, that takes the size limit and returns an

object that will obey that limit.

Listing 9.5 Size-limited stack FIXO using reify

(defn fixed-fixo

([limit] (fixed-fixo limit []))

([limit vector]

(reify FIXO

(fixo-push [this value]

(if (< (count vector) limit)

(fixed-fixo limit (conj vector value))

this))

(fixo-peek [_]

(peek vector))

(fixo-pop [_]

(pop vector)))))

Just like the extend forms, reify has method arglists that include the object itself. It’s

idiomatic to use name the argument this in methods where you need to use it and _

in methods where you ignore its value. But both these conventions should only be fol-

lowed where natural.

NAMESPACED METHODS

A rough analogy can be drawn between protocols and Java interfaces.7 We’ve noted

some of the differences already, but it can be a useful analogy nonetheless. In such a

comparison, where record types are concrete classes, you might see that Java packages

and C++ namespaces are each like Clojure namespaces. It’s normal in all three of

these environments for the interface and the class to each be in a namespace, and not

necessarily the same one. For example, probably few readers were surprised to see

that when we made the class IPersistentVector extend the protocol user/FIXO, they

were each from a different namespace or package.

One way this analogy breaks down is that methods of the protocol itself are

namespaced in a way that Java and C++ interfaces aren’t. In those languages, all meth-

ods of a class share the same effective namespace, regardless of interfaces they’re

implementing. In Clojure, the methods always use the same namespace as the proto-

col itself, which means a record or type can extend (via extend, extend-type, and so

on) identically named methods of two different protocols without any ambiguity. This

is a subtle feature, but it allows you to avoid a whole category of issues that can come

up when trying to combine third-party libraries into a single codebase.

Note that because the methods share the namespace of their protocol, you can’t

have identically named methods in two different protocols if those protocols are in

the same namespace. Because both are under the control of the same person, it’s easy

7 Those of you familiar with Haskell might recognize analogies to its typeclasses in our discussion.

Download from Wow! eBook <www.wowebook.com>

Types, protocols, and records

199

to resolve this by moving one of the protocols to a different namespace or using more

specific method names.

METHOD IMPLEMENTATIONS IN DEFRECORD

We’ve already shown how both protocols and interfaces can be extended to record

types using the various extend forms, but there’s another way to achieve similar

results. Protocol and interface method implementations can be written directly inside

a defrecord form, which ends up looking like the following.

Listing 9.6 Method implementations in defrecord

(defrecord TreeNode [val l r]

FIXO

Implement FIXO

(fixo-push [t v]

methods inline

(if (< v val)

(TreeNode. val (fixo-push l v) r)

(TreeNode. val l (fixo-push r v))))

(fixo-peek [t]

(if l

(fixo-peek l)

Call method instead

val))

of using recur

(fixo-pop [t]

(if l

(TreeNode. val (fixo-pop l) r)

r)))

(def sample-tree2 (reduce fixo-push (TreeNode. 3 nil nil) [5 2 4 6]))

(xseq sample-tree2)

;=> (2 3 4 5 6)

This isn’t only more convenient in many cases, but it can also produce dramatically

faster code. Calling a protocol method like fixo-peek on a record type that imple-

ments it inline can be several times faster than calling the same method on an object

that implements it via an extend form. Also note that the fields of the object are now

available as locals—we use val instead of (:val t).

Polymorphism and recur

Throughout this section, we’ve implemented the fixo-peek function using different

methodologies, but a more subtle difference is worth noting. The first implementation

uses recur for its recursive call as shown:

(fixo-peek [node]

(if (:l node)

(recur (:l node))

(:val node)))

Because of the nature of recur, the first implementation of fixo-peek isn’t polymor-

phic on the recursive call. But the second version of fixo-peek uses a different

approach:

Download from Wow! eBook <www.wowebook.com>

200

CHAPTER 9 Combining data and code

(continued)

(fixo-peek [t]

(if l

(fixo-peek l)

val))

You’ll notice that the recursive call in the second implementation is direct (mundane)

and as a result is polymorphic. In the course of writing your own programs, this dif-

ference will probably not cause issues, but it’s worth storing in the back of your mind.

Putting method definitions inside the defrecord form also allows you to implement

Java interfaces and extend java.lang.Object, which isn’t possible using any extend

form. Because interface methods can accept and return primitive values as well as

boxed objects, implementations of these in defrecord can also support primitives.

This is important for interoperability and can provide ultimate performance parity

with Java code.

We do need to note one detail of these inline method definitions in relation to

recur. Specifically, uses of recur in these definitions can’t provide a new target object:

the initial argument will get the same value as the initial (non-recur) call to the

method. For example, fixo-push takes args t and v, so if it used recur, only a single

parameter would be given: the new value for the v arg.

9.3.3

Building from a more primitive base with deftype

You may have noticed we’ve been using our own function xseq throughout the exam-

ples in this section, instead of Clojure’s seq. This shouldn’t be necessary, as Clojure

provides an ISeqable interface that its seq function can use—all we need to do is to

have our own type implement ISeqable. But an attempt to do this with defrecord is

doomed:

(defrecord InfiniteConstant [i]

clojure.lang.ISeq

(seq [this]

(lazy-seq (cons i (seq this)))))

; java.lang.ClassFormatError: Duplicate method

; name&signature in class file user/InfiniteConstant

This is because record types are maps and implement everything maps should—seq

along with assoc, dissoc, get, and so forth. Because these are provided for us, we

can’t implement them again ourselves, and thus the preceding exception. For the rare

case where you’re building your own data structure instead of just creating applica-

tion-level record types, Clojure provides a lower-level deftype construct that’s similar

to defrecord but doesn’t implement anything at all, so implementing seq won’t con-

flict with anything:

(deftype InfiniteConstant [i]

clojure.lang.ISeq

(seq [this]

Download from Wow! eBook <www.wowebook.com>

Types, protocols, and records

201

(lazy-seq (cons i (seq this)))))

(take 3 (InfiniteConstant. 5))

;=> (5 5 5)

But that also means that keyword lookups, assoc, dissoc, and so on will remain unim-

plemented unless we implement them ourselves:

(:i (InfiniteConstant. 5))

;=> nil

The fields we declared are still public and accessible (although you should try to avoid

naming them the same at the methods in java.lang.Object); they just require nor-

mal Java interop forms to get at them:

(.i (InfiniteConstant. 5))

;=> 5

With all that in mind, the following listing is a final implementation of TreeNode using

deftype, which lets us implement not only ISeq so that we can use seq instead of

xseq, but also IPersistentStack so we can use peek, pop, and conj as well as the

fixo- versions.

Listing 9.7 Implementing map interfaces with deftype

(deftype TreeNode [val l r]

FIXO

Implement FIXO

(fixo-push [_ v]

methods inline

(if (< v val)

(TreeNode. val (fixo-push l v) r)

(TreeNode. val l (fixo-push r v))))

(fixo-peek [_]

(if l

(fixo-peek l)

Call method instead

val))

of using recur

(fixo-pop [_]

(if l

(TreeNode. val (fixo-pop l) r)

r))

clojure.lang.IPersistentStack

Implement interfaces

(cons [this v] (fixo-push this v))

(peek [this] (fixo-peek this))

(pop [this] (fixo-pop this))

clojure.lang.Seqable

(seq [t]

(concat (seq l) [val] (seq r))))

(extend-type nil

FIXO

(fixo-push [t v]

Redefine to use

(TreeNode. v nil nil)))

new TreeNode

(def sample-tree2 (into (TreeNode. 3 nil nil) [5 2 4 6]))

(seq sample-tree2)

;=> (2 3 4 5 6)

Download from Wow! eBook <www.wowebook.com>

202

CHAPTER 9 Combining data and code

One final note about deftype—it’s the one mechanism by which Clojure lets you cre-

ate classes with volatile and mutable fields. We won’t go into it here because using

such classes is almost never the right solution. Only when you’ve learned how Clojure

approaches identity and state, how to use reference types, what it means for a field to

be volatile, and all the pitfalls related to that, should you even consider creating

classes with mutable fields. By then, you’ll have no problem understanding the official

docs for deftype, and you won’t need any help from us.

None of the examples we’ve shown in this section come close to the flexibility of

multimethods. All protocol methods dispatch on just the type of the first argument.

This is because that’s what Java is good at doing quickly, and in many cases it’s all the

polymorphism that’s needed. Clojure once again takes the practical route and makes

the highest-performance mechanisms available via protocols, while providing more

dynamic behavior than Java does and leaving multimethods on the table for when ulti-

mate flexibility is required.

9.4

Putting it all together: a fluent builder for chess moves

People have been known to say that Java is a verbose programming language. This

may be true when compared to the Lisp family of languages, but considerable mind-

share has been devoted to devising ways to mitigate its verbosity. One popular tech-

nique is known as the fluent builder (Fowler 2005) and can be summed up as the

chaining of Java methods to form a more readable and agile instance construction

technique. In this section, we’ll show a simple example of a fluent builder supporting

the construction of chess move descriptions. We’ll then explain how such a technique

is unnecessary within Clojure and instead present an alternative approach that’s sim-

pler, concise, and more extensible. We’ll leverage Clojure’s records in the final solu-

tion, illustrating that Java’s class-based paradigm is counter to Clojure’s basic

principles and often overkill for Java programs.

9.4.1

Java implementation

We’ll start by identifying all of the component parts of a Move class including from and

to squares, a flag indicating whether the move is a castling move, and also the desired

promotion piece if applicable. In order to constrain the discussion, we’ll limit our

idea of a Move to those elements listed. The next step would be to create a simple class

with its properties and a set of constructors, each taking some combination of the

expected properties. We’d then generate a set of accessors for the properties, but not

their corresponding mutators, because it’s probably best for the move instances to be

immutable.

Having created this simple class and rolled it out to the customers of the chess

move API, we begin to notice that our users are sending into the constructor the to

string before the from string, which is sometimes placed after the promotion, and so

on. After some months of intense design and weeks of development and testing, we

release the following elided chess move class:

Download from Wow! eBook <www.wowebook.com>

Putting it all together: a fluent builder for chess moves

203

public class FluentMove {

String from, to, promotion = "";

boolean castlep;

public static MoveBuilder desc() { return new MoveBuilder(); }

public String toString() {

return "Move " + from +

" to " + to +

(castlep ? " castle" : "") +

(promotion.length() != 0 ? " promote to " + promotion : "");

}

public static final class MoveBuilder {

FluentMove move = new FluentMove();

public MoveBuilder from(String from) {

move.from = from; return this;

}

public MoveBuilder to(String to) {

move.to = to; return this;

}

public MoveBuilder castle() {

move.castlep = true; return this;

}

public MoveBuilder promoteTo(String promotion) {

move.promotion = promotion; return this;

}

public FluentMove build() { return move; }

}

}

For brevity’s sake, our code has a lot of holes, such as missing checks for fence post

errors, null, empty strings, assertions, and invariants; it does allow us to illustrate that

the code provides a fluent builder given the following main method:

public static void main(String[] args) {

FluentMove move = FluentMove.desc()

.from("e2")

.to("e4").build();

System.out.println(move);

move = FluentMove.desc()

.from("a1")

.to("c1")

.castle().build();

System.out.println(move);

move = FluentMove.desc()

.from("a7")

.to("a8")

.promoteTo("Q").build();

System.out.println(move);

}

Download from Wow! eBook <www.wowebook.com>

204

CHAPTER 9 Combining data and code

// Move e2 to e4

// Move a1 to c1 castle

// Move a7 to a8 promote to Q

The original constructor ambiguities have disappeared, with the only trade-off being a

slight increase in complexity of the implementation and the breaking of the common

Java getter/setter idioms—both of which we’re willing to live with. But if we’d started

the chess move API as a Clojure project, the code would likely be a very different expe-

rience for the end user.

9.4.2

Clojure implementation

In lieu of Java’s class-based approach, Clojure provides a core set of collection types,

and as you might guess, its map type is a nice candidate for move representation:

{:from "e7", :to "e8", :castle? false, :promotion \Q}

Simple, no?

In a language like Java, it’s common to represent everything as a class—to do oth-

erwise is either inefficient, non-idiomatic, or outright taboo. Clojure prefers simplifi-

cation, providing a set of composite types perfect for representing most categories of

problems typically handled by class hierarchies. Using Clojure’s composite types

makes sense for one simple reason: existing functions, built on a sequence abstrac-

tion, just work:

(defn build-move [& pieces]

(apply hash-map pieces))

(build-move :from "e7" :to "e8" :promotion \Q)

;=> {:from "e7", :to "e8", :promotion \Q}

In two lines, we’ve effectively replaced the Java implementation with an analogous, yet

more flexible representation. The term domain-specific language (DSL) is often thrown

around to describe code such as build-move, but to Clojure (and Lisps in general) the

line between DSL and API is blurred. In the original FluentMove class, we required a

cornucopia of code in order to ensure the API was agnostic of the ordering of move ele-

ments; using a map, we get that for free. Additionally, FluentMove, though relatively

concise, was still bound by fundamental Java syntactical and semantic constraints.

There’s one major problem with our implementation—it doesn’t totally replace

the Java solution. If you recall, the Java solution utilized the toString method to print

its representative form. The existence of a polymorphic print facility in Java is nice,

and it allows a class creator to define a default print representation for an object when

sent to any Java print stream. This means that the same representation is used on the

console, in log files, and so on. Using raw maps can’t give us this same behavior, so

how can we solve this problem?

Download from Wow! eBook <www.wowebook.com>

Putting it all together: a fluent builder for chess moves

205

USING RECORDS

If we instead use a record, then the solution is as simple as that shown next.

Listing 9.8 A chess move record

(defrecord Move [from to castle? promotion]

Object

(toString [this]

(str "Move " (:from this)

" to " (:to this)

(if (:castle? this) " castle"

(if-let [p (:promotion this)]

(str " promote to " p)

"")))))

As we mentioned in the previous section, within the body of a record we can take up

to two actions: participate in a protocol, or override any of the methods in the

java.lang.Object class. For the Move record, we override toString in order to allow

it to participate in Java’s overarching polymorphic print facility, as shown:

(str (Move. "e2" "e4" nil nil))

;=> "Move e2 to e4"

(.println System/out (Move. "e7" "e8" nil \Q))

; Move e7 to e8 promote to Q

We’ve once again gone back to positional construction using records, but as we’ll

show, Clojure even has an answer for this.

SEPARATION OF CONCERNS

Both FluentMove and build-move make enormous assumptions about the form of the

data supplied to them and do no validation of the input. For FluentMove, object-

oriented principles dictate that the validation of a well-formed move (not a legal

move, mind you) should be determined by the class itself. There are a number of

problems with this approach, the most obvious being that to determine whether a

move is well-formed, the class needs information about the rules of chess. We can

rewrite FluentMove to throw an exception to prevent illegal moves from being con-

structed, but the root problem still remains—FluentMove instances are too smart. Per-

haps you don’t see this as a problem, but if we were to extend our API to include other

aspects of the game of chess, then we’ll find that bits of overlapping chess knowledge

would be scattered throughout the class hierarchy. By viewing the move structure as a

value, Clojure code provides some freedom in the implementation of a total solution,

as shown:

(defn build-move [& {:keys [from to castle? promotion]}]

{:pre [from to]}

(Move. from to castle? promotion))

(str (build-move :from "e2" :to "e4"))

;=> "Move e2 to e4"

Download from Wow! eBook <www.wowebook.com>

206

CHAPTER 9 Combining data and code

By wrapping the Move constructor in a build-move function, we put the smarts of con-

structing moves there instead of in the type itself. In addition, using a precondition,

we specified the required fields, and by using Clojure’s named parameters and argu-

ment destructuring we’ve again ensured argument order independence. As a final

added advantage, Clojure’s records are maps and as a result can operate in almost

every circumstance where a map would. As author Rich Hickey proclaimed, any new

class in general is itself an island, unusable by any existing code written by anyone,

anywhere. So our point is this: consider throwing the baby out with the bath water.

9.5

Summary

Clojure disavows the typical object-oriented model of development. But that’s not to

say that it completely dismisses all that OOP stands for. Instead, Clojure wholeheartedly

touts the virtues of interface-oriented programming (or abstraction-oriented program-

ming, as we’ve called it), in addition to runtime polymorphism. But in both cases, the

way that Clojure presents these familiar topics is quite different from what you might

be accustomed to. In almost every circumstance, Clojure’s abstraction-oriented facili-

ties will sufficiently represent your problem domain, but there may be times when they

simply can’t. We’ll preach the virtues of abstractions more throughout the rest of the

book, but for now we’re compelled to take a side path into an explorations of Java

interoperability.

Download from Wow! eBook <www.wowebook.com>

Java.next

This chapter covers

 Generating objects on the fly with proxy

 Clojure gen-class and GUI programming

 Clojure’s relationship to Java arrays

 All Clojure functions implement...

 Using Clojure data structures in Java APIs

 definterface

 Be wary of exceptions

Regardless of your views on the Java language itself, it’s difficult to deny that the JVM

is a stellar piece of software. The confluence of the just-in-time (JIT) compiler, gar-

bage collection, HotSpot, and the flexible bytecode have created an environment

that many programmers have chosen to grow their alternative programming lan-

guages. Additionally, the deluge of library options hosted on the JVM further make

the JVM the language target of choice. From Clojure to Groovy to Scala to Fantom to

Frink to Ioke to Jess to JRuby to Jython, there seems to be no lack of options for the

enthusiastic polyglot programmer. We may soon see job listings for “JVM program-

mers.” But where does that leave Java the programming language?

Java the language isn’t dead.

207

Download from Wow! eBook <www.wowebook.com>

208

CHAPTER 10 Java.next

The JVM is optimized for running Java bytecode, and only recently1 have Java.next

languages been a consideration. You may ask yourself whether JVM bytecode is equiva-

lent to Java source code, and the answer is no. Instead, languages such as Clojure and

Scala compile directly to bytecode and can access Java compiled libraries as needed.

Because of their reliance on the JVM as the runtime environment, Clojure and the

other Java.next languages will be fundamentally constrained by the limitations of the

JVM itself. The limitations of the JVM as defined by the limitations of the Java language

specification set the beat by which the Java.next languages dance. Java isn’t dead; it’s

alive and well, and it runs the show.

THE JAVA.NEXT MANTRA The apprentice avoids all use of Java classes. The jour-

neyman embraces Java classes. The master knows which classes to embrace

and which to avoid.

An expert understanding of the Java Virtual Machine isn’t required for writing power-

ful applications in Clojure, but it’ll help when issues stemming from host limitations

arise. Thankfully, Clojure does a good job of mitigating many of the limitations inher-

ent in its host, but some are too deeply embedded in the fibers of the JVM to avoid. Clo-

jure provides a specific set of interoperability tools: gen-class, proxy, definterface,

its exceptions facility, and a host of array functions. We’ll touch on each of these in

turn, but we’ll begin with the creation of anonymous objects using proxy.

10.1

Generating objects on the fly with proxy

There’s a saying within the Clojure community stating (Halloway 2009) that Clojure

does Java better than Java. This is a bold statement, but not one without merit, as we’ll

show throughout this chapter. Java programmers are accustomed to drawing a severe

distinction between development time and runtime. Using Clojure’s proxy feature

allows you to blur this distinction.

CLOJURE APHORISM Many software projects require a lot of planning because

their implementation languages don’t foster change. Clojure makes it a lot

easier to plan for change.

Clojure’s proxy mechanism is meant strictly for interoperability purposes. In section

9.3, we discussed how reify was intended to realize a single instance of a type, proto-

col, or interface—in other words, abstractions. But when dealing with Java libraries,

you’re at times required to extend concrete classes, and it’s in this circumstance where

proxy shines. Be aware that by using proxy, you bring a lot of Java’s semantics into

your Clojure programs. Though extending concrete classes is seen often in Java,

doing so in Clojure is considered poor design, leading to fragility, and should there-

fore be restricted to those instances where interoperability demands it.

1 More details can be found in JSR-000292, “Supporting Dynamically Typed Languages on the Java Platform.”

Download from Wow! eBook <www.wowebook.com>

Generating objects on the fly with proxy

209

10.1.1 A simple dynamic web service

Using Clojure breaks the ponderous code/compile/run development cycle by adding

an element of dynamism into the fold. Take for example a scenario where we want to

develop a web service using an existing Java 1.5 API.

Listing 10.1 A simple dynamic web service

(ns joy.web

(:import (com.sun.net.httpserver HttpHandler HttpExchange HttpServer)

(java.net InetSocketAddress HttpURLConnection)

(java.io IOException FilterOutputStream)

(java.util Arrays)))

(defn new-server [port path handler]

Create service

(doto (HttpServer/create (InetSocketAddress. port) 0)

(.createContext path handler)

(.setExecutor nil)

(.start)))

(defn default-handler [txt]

Print hello message

(proxy [HttpHandler] []

(handle [exchange]

(.sendResponseHeaders exchange HttpURLConnection/HTTP_OK 0)

(doto (.getResponseBody exchange)

(.write (.getBytes txt))

Close over txt

(.close)))))

(def server (new-server 8123

"/joy/hello"

(default-handler "Hello Cleveland")))

After entering the code in listing 10.1, you should see the message “Hello Cleveland”

in your web browser at address http://localhost:8123/joy/hello. This is only margin-ally interesting, especially because the source is organized in a way that doesn’t take

advantage of Clojure’s flexibility.

If we instead organize the code to bind the return of default-handler, we can

manipulate the handler independently and update its behavior at runtime, as shown:

(.stop server 0)

(def p (default-handler

"There's no problem that can't be solved

with another level of indirection"))

(def server (new-server 8123 "/joy/hello" p))

At this point, visiting the aforementioned URL will show the new message, making this

simple server more compelling. But we can take it one step further by making changes

without taking the server instance down in such a clumsy fashion. Ideally, we’d like to

be able to call a function to change the message at any time:

(change-message p "Our new message")

The implementation of change-message is given in the following listing.

Download from Wow! eBook <www.wowebook.com>

210

CHAPTER 10 Java.next

Listing 10.2 Convenience functions for changing the web service message

(defn make-handler-fn [fltr txt]

(fn [this exchange]

Name explicit this

(let [b (.getBytes txt)]

(-> exchange

.getResponseHeaders

(.set "Content-Type" "text/html"))

(.sendResponseHeaders exchange

HttpURLConnection/HTTP_OK

0)

(doto (fltr (.getResponseBody exchange))

Pass through filter

(.write b)

(.close)))))

(defn change-message

"Convenience method to change a proxy's output message"

([p txt] (change-message p identity txt))

Use identity filter

([p fltr txt]

(update-proxy p

{"handle" (make-handler-fn fltr txt)})))

We’ve added a few extras to the implementation that will be useful later, but for now

concentrate on the fact that change-message calls the function update-proxy with

the proxy object p and a map containing an anonymous function keyed on a string

referencing a method name to override. The anonymous function looks similar to the

handle method defined in the returned proxy from the original default-handler

function, with some extras added for flexibility’s sake. You can test this by entering the

following function call:

(change-message p "Hello Dynamic!")

Refreshing your browser will reflect the change made by displaying the string "Hello

Dynamic!". If so inclined, you can also inspect the current proxy mappings using the

function proxy-mappings. The question remains—how does update-proxy change

the behavior of a previously generated proxy class?

IT’S CALLED PROXY FOR A REASON

As we mentioned, the proxy function generates the bytecode for an actual class on

demand, but it does so in such a way to provide a more dynamic implementation.

Instead of inserting the bytecode for the given function bodies directly into the proxy

class, Clojure instead generates a proper proxy in which each method looks up the

function implementing a method in a map. This trades highly useful dynamic behav-

ior for some runtime cost, but in many cases this is a fair trade.

Based on the method name, the corresponding function is retrieved from a map

and invoked with the this reference and the argument(s).

PROXIES FOR TRUE POWER DYNAMISM

Working from the abstract model in figure 10.1, observe how Clojure updates the

mapped functions within a proxy at runtime. This web service is a humble example,

but there’s a point to take away from this exercise: to perform this same task in Java

Download from Wow! eBook <www.wowebook.com>

Generating objects on the fly with proxy

211

isn’t impossible but would require an enormous

p

amount of scaffolding to implement properly,

this = HttpHandler.

whereas in Clojure it’s built into the language.

handle(exchange)

PROXIES AS PROPER CITIZENS

In the original change-message function, we

provided a hook named fltr that took the result

(fn [this exchange])

of the call to the .getResponseBody method.

Because the result of this method call is a

java.io. OutputStream, we can use that infor-

mation to our advantage when creating a filtering

function. The use of the identity function as the

Figure 10.1 Proxy lookup: the

default filter ensures that the usage doesn’t break

instance returned by proxy is a proper

proxy that does method dispatch to

in the default case; but if we’re to utilize our own

functions in a lookup table. These

filtering function, we must ensure that we prop-

functions can therefore be swapped out

erly wrap the original, which again is a perfect use

with replacements as needed.

case for proxy. A simple implementation of a

screaming-filter would be implemented as such:

(defn screaming-filter [o]

(proxy [FilterOutputStream] [o]

(write [b]

(proxy-super write (.getBytes (str "<strong>"

(.toUpperCase (String. b))

"</strong>"))))))

The proxy returned by screaming-filter extends the Java class java.io.Filter-

OutputStream to the superclass constructor (via the [o] vector). It passes the argument

o, which corresponds to the OutputStream obtained from the .getResponseBody

method.

ANAPHORIC PROXY In section 8.5, we mentioned that it’s non-idiomatic to

write anaphoric macros, yet you might’ve noticed that proxy is a contradic-

tion of that statement. The use of the anaphora this is subject to the same

nesting limitations as previously mentioned and is a good candidate for

change in later versions of Clojure. You might notice that the reify macro,

though similar to proxy, doesn’t use an anaphoric this but instead requires

that it be named explicitly—the preferred approach for your own, and likely

the way forward for all future Clojure core macros.

The call to the proxy-super function is similar to Java’s super.method() semantics. If

we now execute the call to change-message passing in screaming-filter, we’ll see

the expected filtered message in all caps and bold on a browser refresh:

(change-message p screaming-filter "whisper")

Note that in a break from almost every other construct in Clojure, proxy-super is not

thread-safe. If some other thread were to call this proxy instance’s write method

while proxy-super was still running, the base class’s method would be called directly,

Download from Wow! eBook <www.wowebook.com>

212

CHAPTER 10 Java.next

incorrectly skipping the proxy implementation. So be careful using proxy-super and

multiple threads in close proximity to each other.

FINAL POINTS ABOUT PROXY

Clojure’s proxy capabilities are truly dynamic, allowing you to create fully stubbed

proxies using either construct-proxy, get-proxy-class, or init-proxy. In both

cases, a partially to fully realized proxy will be constructed, allowing programmatic

customization using update-proxy and arbitrary mixin maps.

There’s a universe of difference between the code outlined in this subsection and

systems employing true code hot-loading, but it’s a reasonable facsimile. Using proxy

is powerful, but doing so creates unnamed instances unavailable for later extension. If

you instead wish to create named classes then you’ll need to use Clojure’s gen-class

mechanism, which we’ll discuss next.

10.2

Clojure gen-class and GUI programming

In section 9.1, we mentioned that Clojure namespaces can be used as the basis for

generating a named class. In this section, we’ll address this topic and others related to

Clojure’s gen-class function and :gen-class namespace directive in the context of

writing a simple graphical user interface (GUI) library.

10.2.1 Namespaces as class specifications

Similarly to the ns example in section 9.1, the explanation of gen-class begs a declar-

ative approach for a namespace defining a class named joy.gui.DynaFrame. We’d like

this class to extend javax.swing.JFrame and declare the Vars providing its overriding

method implementations to be prefixed2 by the symbol df-. In addition, we’d like the

class to implement the clojure.lang.IMeta interface. We’d also like a place to store

information about instances of this class in state and would like the initialization func-

tion called on construction to be named df-init. We’d like to define a single construc-

tor, taking a string and passing it onto the superclass constructor also taking a string.

We then want to declare two public methods: the first named display taking a

java.awt.Container and returning void, and the second static method version tak-

ing no arguments and returning a string. Finally, we’ll declare the required imports

needed.

The worded DynaFrame class declaration is complex but has the advantage of hav-

ing a direct code translation, as shown next.

Listing 10.3 The DynaFrame class namespace declaration

(ns joy.gui.DynaFrame

(:gen-class

:name joy.gui.DynaFrame

:extends javax.swing.JFrame

Superclass

:implements [clojure.lang.IMeta]

Interface

:prefix df-

2 If you don’t specify a :prefix, then the default - will be used.

Download from Wow! eBook <www.wowebook.com>

Clojure gen-class and GUI programming

213

:state state

Instance state

:init init

Init function

Constructor

:constructors {[String] [String]}

mapping

:methods [[display [java.awt.Container] void]

Public

^{:static true} [version [] String]])

method

(:import (javax.swing JFrame JPanel)

Static method

(java.awt BorderLayout Container)))

You can compile this namespace by saving it in a directory joy/gui, located on the

classpath, in a file named DynaFrame.clj and executing the function (compile

'joy.gui.DynaFrame) in a fresh REPL. This allows a compiled class to be immediately

available. But trying to create an instance in the same REPL will prove fruitless:

(joy.gui.DynaFrame. "1st")

; java.lang.UnsupportedOperationException:

; joy.gui.DynaFrame/df-init not defined

Clearly we haven’t defined the df-init function, so we’ll do that now by switching to

the joy.gui.DynaFrame namespace, defining it outright:

(in-ns 'joy.gui.DynaFrame)

(defn df-init [title]

[[title] (atom {::title title})])

Now run the following in your REPL:

(joy.gui.DynaFrame. "2nd")

; java.lang.UnsupportedOperationException:

; meta (joy.gui.DynaFrame/df-meta not defined?)

Because we told the Clojure compiler that the class should implement the IMeta inter-

face, we should’ve provided a concrete implementation, which you can do at the REPL:

(defn df-meta [this] @(.state this))

(defn version [] "1.0")

As an added bonus, we implemented the static method version. To see the effects of

these functions, execute the following:

(meta (joy.gui.DynaFrame. "3rd"))

;=> {:joy.gui.DynaFrame/title "3rd"}

(joy.gui.DynaFrame/version)

;=> "1.0"

We’ve filled in most of the implementation of the DynaFrame class except for the

display function, which you can implement as follows:

(defn df-display [this pane]

(doto this

(-> .getContentPane .removeAll)

(.setContentPane (doto (JPanel.)

(.add pane BorderLayout/CENTER)))

(.pack)

(.setVisible true)))

Download from Wow! eBook <www.wowebook.com>

214

CHAPTER 10 Java.next

Figure 10.2 A simple use of DynaFrame: now

Figure 10.3 A simple dynamic update of

that you’ve compiled the DynaFrame class,

DynaFrame: we can update the

you can start using it to display simple GUIs.

DynaFrame on the fly without restarting.

You can see df-display in action within the REPL by running the following:

(def gui (joy.gui.DynaFrame. "4th"))

(.display gui (doto (javax.swing.JPanel.)

(.add (javax.swing.JLabel. "Charlemagne and Pippin"))))

This will now display the GUI frame seen in figure 10.2.

And because it’s a DynaFrame we should be able to change it on the fly, right? Right:

(.display gui (doto (javax.swing.JPanel.)

(.add (javax.swing.JLabel. "Mater semper certa est." ))))

This will change the view to that in figure 10.3.

But now that you have this interesting little frame, what can you do with it? Next,

we’ll experiment with DynaFrame as the foundation for agile GUI prototyping.

THE GUTS OF NAMESPACE COMPILATION

So what exactly does the :gen-class directive provide in terms of generated class

files? With or without :gen-class, Clojure will generate a set of classes correspond-

ing to each function in a namespace. For the function joy.gui.DynaFrame/df-dis-

play, a class file will be generated on the classpath of joy.gui.DynaFrame

$df_display containing (at least) a method invoke, at the location CLASSPATH/

joy/gui/DynaFrame$df_display.class, as shown:

package joy.gui;

public class DynaFrame$df_display extends AFunction {

. . .

public Object invoke(Object that, Object container) {

. . . display actions . . .

}