Foreword to the Second Edition

In this second edition of The Joy of Clojure, Michael Fogus and Chris Houser present a cornucopia of programming concepts, including many of the topics from the programming languages course we taught together for many years. Fundamental programming languages concepts close to our hearts that appear in this book include higher-order functions, lexical scope, closures, tail recursion, mutual recursion, continuations and continuation-passing style, trampolining, lazy sequences, macros, and relational programming. Most important, Fogus and Houser teach you how to define your own little languages.

Alan J. Perlis, brilliant language designer and inaugural Turing Award recipient, famously wrote, “There will always be things we wish to say in our programs that in all known languages can only be said poorly.” No existing programming language can express precisely those concepts and abstractions needed for your specific application. The only person who can design a language to solve your exact problem is you.

Creating a little language to solve a specific problem is the most effective technique yet devised for reducing complexity in software.[1] Two well-known examples are database query languages and the formula languages of spreadsheet applications. These examples are as notable for what they exclude as for what they include, illustrating another of Perlis’s epigrams: “A programming language is low level when its programs require attention to the irrelevant.” By only including features relevant to the problem, a well-designed little language is inherently high level.

1 Jon Bentley popularized the concept of little languages in his article “Programming Pearls: Little Languages,” Communications of the ACM 29, no. 8 (1986):711-21.

Database query languages illustrate another fundamental aspect of little languages: writing a complete application requires addressing problems in more than one domain. An application that performs database queries will also make use of other languages. A single little language can’t address the exact needs of a nontrivial application any more than can a single general-purpose language.

For this reason, little languages work best in concert. The ideal technique for writing a complex program is to slice it into multiple problem-specific pieces and then define a language for each problem slice. If we slice the program vertically, the result is a “tower” of languages, layered atop one another. Regardless of how we slice the overall problem, we can use the right language, and the right paradigm, for each subproblem.

As with recursion, the art of defining little languages encourages—and rewards—wishful thinking. You might think to yourself, “If only I had a language for expressing the rules for legal passwords for my login system.” A more involved example—a story, really—started several years ago, when we thought to ourselves, “If only we had the right relational language, we could write a Lisp interpreter that runs backward.”[2] What does this mean?

2 We use Lisp to refer to any member of a large family of languages that includes Scheme, Racket, Common Lisp, Dylan, and, of course, Clojure. To us, a Lisp must be homoiconic, have first-class functions, and have some form of macros. (All three concepts are described in this book.)

An interpreter can be thought of as a function that maps an input expression, such as (+ 5 1), onto a value—in this case, 6. We wanted to write an interpreter in the style of a relational database, in which either the expression being interpreted or the value of that expression, or both, can be treated as unknown variables. We can run the interpreter forward using the query (interpret '(+ 5 1) x), which associates the query variable x with the value 6. Better yet, we can run the interpreter backward with the query (interpret x 6), which associates x with an infinite stream of expressions that evaluate to 6, including (+ 5 1) and ((lambda (n) (* n 2)) 3). (Brainteaser: determine the behavior of the query (interpret x x).)

Implementing a relational interpreter is tricky, but doing so can be made easier by using a little language specifically designed for relational programming. In the end, our wishful thinking led us to build a tower of languages: a relational Lisp interpreter, on top of a rich relational language, on top of a minimal relational language, on top of a rich functional language, on top of a minimal functional language.[3] (The Lisp interpreter accepts a minimal functional language, turning the tower of languages into a circle!) Given the power of this approach, it isn’t surprising that many Lisp implementations—including the Clojure compiler—are built as layers of languages.

3 By relational language, we mean a pure logic programming language; or, as in this example, a pure constraint logic programming language.

Using what you’ll learn from Fogus and Houser in The Joy of Clojure, you can begin building your own towers of languages, each with its own syntactic forms and evaluation rules, tailored to your specific problem domains. No technique for software development is more expressive or more joyful.

WILLIAM E. BYRD AND DANIEL P. FRIEDMAN

Authors of The Reasoned Schemer (MIT Press, 2005)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.23.92.78