Design

The Clojure Philosophy

Simplicity, freedom to focus, empowerment, consistency, and clarity: Nearly every element of the Clojure programming language is designed to promote these goals.

Extreme Flexibility

Why has Lisp persevered for more than 50 years while countless other languages have come and gone? There are probably complex
reasons, but chief among them is likely the fact that Lisp as a language genotype fosters language flexibility in the extreme. Newcomers to Lisp are sometimes unnerved by its pervasive use of parentheses
and prefix notation, which is different than non-Lisp programming languages. The regularity of this behavior not only reduces
the number of syntax rules you have to remember, but also makes the writing of macros trivial. Let's take a look at one now. It's an example that we'll get working on in a moment:

We hope some of those words look familiar to you, because this isn't an article on SQL. Regardless, our point here is that Clojure
doesn't have SQL support built in. The words SELECT, FROM, and so forth aren't built-in forms. They're also not regular functions, because if SELECT were, then the use of a, b, and c would be an error, because they haven't been defined yet.

So what does it take to define a domain-specific language (DSL) like this in Clojure? Well, it's not production-ready code
and doesn't tie into any real database servers; but with just one macro and the three functions shown in Lisiting One, the preceding query returns these handy values:

Note that some words such as FROM and ON are taken directly from the input expression, whereas others such as ~max and AND are treated specially. The max that was given the value 5 when the query was called is extracted from the literal SQL string and provided in a separate vector, perfect for using in
a prepared query in a way that will guard against SQL-injection attacks. The AND form was converted from the prefix notation of Clojure to the infix notation required by SQL.

In Listing One, on line 2, we use core string functions.
On line 6, we handle unsafe literals. On line 9-11, we convert prefix to infix. On lines 13-15, we support each kind of clause. On line 28, we call the appropriate converter. And starting on line 31, we provide the main entrypoint macro.

But the point here isn't that this is a particularly good SQL DSL  more complete ones are available. Our point is that once you have the skill to easily create a DSL like this, you'll recognize opportunities to define your
own that solve much narrower, application-specific problems than SQL does. Whether it's a query language for an unusual non-SQL
datastore, a way to express functions in some obscure math discipline, or some other application we as authors can't imagine,
having the flexibility to extend the base language like this, without losing access to any of the language's own features,
is a game-changer.

Although we shouldn't get into too much detail about the implementation, take a brief look back at Listing One and follow along as we discuss important aspects of its implementation.

Reading from the bottom up, you'll notice the main entry point, the SELECT macro. This returns a vector of two items  the first is generated by calling expand-clause, which returns the converted query string, whereas the second is another vector of expressions marked by ~ in the input. The ~ is known as unquote. Also note the use of tree-seq here to succinctly extract items of interest from a tree of values, namely the input expression.

The expand-clause function takes the first word of a clause, looks it up in the clause-map, and calls the appropriate function to do the actual conversion from Clojure s-expression to SQL string. The clause-map provides the specific functionality needed for each part of the SQL expression: inserting commas or other SQL syntax, and
sometimes recursively calling expand-clause when subclauses need to be converted. One of these is the WHERE clause, which handles the general conversion of prefix expressions to the infix form required by SQL by delegating to the
expand-expr function.

Overall, the flexibility of Clojure demonstrated in this example comes largely from the fact that macros accept code forms,
such as the SQL DSL example we showed, and can treat them as data  walking trees, converting values, and more. This works not
only because code can be treated as data, but because in a Clojure program, code is data.

Code is Data

The notion that "code is data" is difficult to grasp at first. Implementing a programming language where code shares the same
footing as its comprising data structures presupposes a fundamental malleability of the language itself. When your language
is represented as the inherent data structures, the language itself can manipulate its own structure and behavior. You may have visions of Ouroboros after reading the previous sentence, and that wouldn't
be inappropriate, because Lisp can be likened to a self-licking lollypop  more formally defined as homoiconicity. Lisp's homoiconicity takes a great conceptual leap in order to fully grasp, but we'll lead you toward that understanding
in hopes that you too will come to realize the inherent power.

Functional Programming

Quick, what does functional programming mean? Wrong answer.

Don't be too discouraged, however  we don't really know the answer either. Functional programming is one of those computing
terms that has a nebulous definition. If you ask 100 programmers for their definition, you'll likely receive 100 different answers.
Sure, some definitions will be similar, but like snowflakes, no two will be exactly the same. To further muddy the waters,
the cognoscenti of computer science will often contradict one another in their own independent definitions. Likewise, the
basic structure of any definition of functional programming will be different depending on whether your answer comes from
someone who favors writing their programs in Haskell, ML, Factor, Unlambda, Ruby, or Qi. How can any person, book, or language claim authority for functional programming? As it turns out, just as the multitudes of unique snowflakes
are all made mostly of water, the core of functional programming across all meanings has its core tenets.

A Workable Definition of Functional Programming

Whether your own definition of functional programming hinges on the lambda calculus, monadic I/O, delegates, or java.lang.Runnable, your basic unit of currency is likely to be some form of procedure, function, or method  herein lies the root. Functional
programming concerns and facilitates the application and composition of functions. Further, for a language to be considered
functional, its notion of function must be first-class. The functions of a language must be able to be stored, passed, and returned just like any other piece of data within that
language. It's beyond this core concept that the definitions branch toward infinity, but thankfully, it's enough to start.
Of course, we'll also present a further definition of Clojure's style of functional programming that includes such topics
as purity, immutability, recursion, laziness, and referential transparency.

The Implications of Functional Programming

Object-oriented programmers and functional programmers will often see and solve a problem in different ways. Whereas an object-oriented
mindset will foster the pproach of defining an application domain as a set of nouns (classes), the functional mind will see the solution as the composition
of verbs (functions). Though both programmers may in all likelihood generate equivalent results, the functional solution will
be more succinct, understandable, and reusable. Grand claims indeed! We hope that you'll agree that
functional programming fosters elegance in programming. It takes a shift in mindset to start from thinking in nouns to arrive
at thinking in verbs, but the journey is worthwhile. In any case, we think there's much that you can take from Clojure
to apply to your chosen language  if only you approach the subject with an open mind.

Why Clojure Isn't Especially Object-Oriented

Elegance and familiarity are orthogonal.  Rich Hickey

Clojure was born out of frustration provoked in large part by the complexities of concurrent programming, complicated by the
weaknesses of object-oriented programming in facilitating it. This section explores these weaknesses and lays the groundwork
for why Clojure is functional and not object-oriented.

Defining Terms

Before we begin, it's useful to define terms. (These terms are also defined and elaborated on in Rich Hickey's presentation, "Are We There Yet?").

The first important term to define is time. Simply put, time refers to the relative moments when events occur. Over time, the properties associated with an entity  both
static and changing, singular or composite  will form a concrescence and be logically deemed its identity. It follows from this that at any given time, a snapshot can be taken of an entity's properties defining its state. This notion of state is an immutable one because it's not defined as a mutation in the entity itself, but only as a manifestation
of its properties at a given moment in time. Imagine a child's flip book to understand the terms fully.
The book itself represents
the identity. Whenever you wish to show a change in the illustration, you draw another picture and add it to the end of your
flip book. The act of flipping the pages therefore represents the states over time of the image within. Stopping at any given
page and observing the particular picture represents the state of the image at that moment in time.

It's important to note that in the canon of object-oriented programming, there's no clear distinction between state and identity.
In other words, these two ideas are conflated into what's commonly referred to as mutable state. The classical object-oriented model allows unrestrained mutation of object properties without a willingness to preserve
historical states. Clojure's implementation attempts to draw a clear separation between an object's state and identity as
they relate to time. To state the difference to Clojure's model in terms of the aforementioned flip book, the mutable state
model is different, so modeling state change with mutation requires that you stock up on erasers. Your book becomes a single
page, requiring that in order to model changes, you must physically erase and redraw the parts of the picture requiring change.
Using this model, you should see that mutation destroys all notion of time, and state and identity become one.

Immutability lies at the cornerstone of Clojure, and much of the implementation ensures that immutability is supported efficiently.
By focusing on immutability, Clojure eliminates entirely the notion of mutable state (which is an oxymoron) and instead expounds that most of what's meant by objects are instead values. Value by definition refers to an object's constant representative amount, magnitude, or epoch. (Some entities have no representative value  Pi is an example. But in the realm of computing, where we're ultimately referring
to finite things, this is a moot point.) You might ask yourself: what are the implications of the value-based programming semantics of
Clojure?

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!