Chapter 8. Macros

If you give someone Fortran, he has Fortran. If you give someone Lisp, he has any language he pleases.

Guy Steele[1]

1 Afterword in The Seasoned Schemer by Daniel P. Friedman and Matthias Felleisen (MIT Press, 1995).

This chapter covers

  • Data is code is data
  • Defining control structures
  • Macros combining forms
  • Using macros to control symbolic resolution time
  • Using macros to manage resources
  • Macros returning functions

Macros are where the rubber of “code is data” meets the road of making programs simpler and cleaner. To fully understand macros, you need to understand the different times of Clojure: read time, macro-expansion time, compile time, and runtime. Macros perform the bulk of their work at compile time. We’ll start by looking at what it means for code to be data and data to be used as code. This is the background you’ll need to understand that control structures in Clojure are built out of macros, and how you can build your own. The mechanics of macros are relatively simple, and before you’re halfway through this chapter you’ll have learned all you technically need to write your own. Where macros get complicated is when you try to bring theoretical knowledge of them into the real world, so to help you combat that we’ll lead you on a tour of practical applications of macros.

What kinds of problems do macros solve? One extremely important role that macros perform is to let you transform an expression into something else, before runtime. Consider Clojure’s -> macro, which returns the result of evaluating a succession of functions on an initial value. To understand the arrow macro, we find it useful to think of it as an arrow indicating the flow of data from one function to another—the form (-> 25 Math/sqrt int list) can be read as follows:

1.  Take the value 25.

2.  Feed it into the method Math/sqrt.

3.  Feed that result into the function int.

4.  Feed that result into the function list. Graphically, this can be viewed as shown in figure 8.1.

Figure 8.1. Arrow macro: each expression is inserted into the following one at compile time, allowing you to write the whole expression inside-out when that feels more natural.

It expands into the following expression:

(list (int (Math/sqrt 25)))

When viewed this way, the -> macro can be said to weave a sequence of forms into each in turn. This weaving can be done in any form and is always stitched in as the first argument to the outermost expression.[2] Observe how the placement of snowmen provides visual markers for the weave point:

2 On the other hand, the ->> macro threads the form as the last argument.

(-> (/ 144 12) (/  2 3) str keyword list)
;=> (:2)

(-> (/ 144 12) (*  4 (/ 2 3)) str keyword (list  :33))
;=> (:32 :33)

As shown via snowman, each expression is inserted into the following one at compile time, allowing you to write the whole expression inside-out. Using one of the arrow macros is useful when many sequential operations need to be applied to a single object. So this is a potential use case for macros: taking one form of an expression and transforming it into another form. In this chapter, we’ll also look at using macros to combine forms, change forms, control evaluation and resolution of arguments, manage resources, and build functions. But first, what does it mean that Clojure code is data, and why should you care?

8.1. Data is code is data

You’re already familiar with textual representations of data in your programs, at least with strings, lists, vectors, maps, and so on. Clojure, like other Lisps, takes this one step further by having programs be made entirely out of data. Function definitions in Clojure programs are also represented using an aggregation of the various data structures mentioned in the previous chapters. Likewise, the expressions representing the execution of functions and the use of control structures are also data structures! These data representations of functions and their executions represent a concept different from the way other programming languages operate. Typically, there’s a sharp distinction between data structures and functions of the language. In fact, most programming languages don’t even remotely describe the form of functions in their textual representations. With Clojure, there’s no distinction between the textual form and the actual form of a program. When a program is the data that composes the program, then you can write programs to write programs. This may seem like nonsense now, but as you’ll see throughout this chapter, it’s powerful.

To start with, look at the built-in Clojure function eval, whose purpose is to take a data structure representing a Clojure expression, evaluate it, and return the result. This behavior can be seen in the following examples:

(eval 42)
;=> 42

(eval '(list 1 2))
;=> (1 2)

(eval (list 1 2))
; java.lang.ClassCastException:
;  java.lang.Integer cannot be cast to clojure.lang.IFn

Why did we get an exception for the last example? The answer lies in the previous example. The quote in '(list 1 2) causes eval to view it as (list 1 2), which is the function call to create the resulting list. Likewise, for the final example eval received a list of (1 2) and attempted to use 1 as a function, thus failing. Not very exciting, is it? The excitement inherent in eval stems from something that we mentioned earlier[3]—if you provide eval a list in the form expected of a function call, then something else should happen. This something else is the evaluation of a function call and not of the data structure itself. Look at what happens if you try evaluating something more complicated:

3 All the way back in section 2.5.

(eval (list (symbol "+") 1 2))
;=> 3

In words, the steps involved are as follows:

1.  The function symbol receives a string + and returns a symbol data type of +.

2.  The function list receives three arguments—a symbol +, the integer 1, and the integer 2—and returns a list of these elements.

3.  The eval function receives a list data type of (+ 1 2), recognizes it as the function call form, and executes the + function with the arguments 1 and 2, returning the integer 3.

8.1.1. Syntax-quote, unquote, and splicing

You’ll often hear the phrase “eval is evil,” but many of the problems that arise from the use of eval occur because the bindings it uses for evaluation are global. If we could instead restrict the use of specific bindings to eval, then perhaps we could convince it to be less evil. A function in the following snippet uses three features first shown in section 2.7—Clojure’s syntax-quote (written as a single back-quote), unquote (written as ~), and unquote-splice (written as ~@) operations—to build a let structure around the form to evaluate:

The bindings created use the interesting `'~v pattern to garner the value of the built bindings at runtime. Observe how contextual-eval works:

(contextual-eval '{a 1, b 2} '(+ a b))
;;=> 3

(contextual-eval '{a 1, b 2} '(let [b 1000] (+ a b)))
;;=> 1001
Handling nested syntax-quotes

Dealing with nested syntax-quotes can at times be complicated. But you can visualize the way in which unquoting affects the nested structures as a result of repeated evaluations (Steele 1990) relative to its nesting level:

(let [x 9, y '(- x)]
  (println `y)
  (println ``y)
  (println ``~y)
  (println ``~~y)
  (contextual-eval {'x 36} ``~~y))
; user/y
; (quote user/y)
; user/y
; (- x)
;=> -36

The nesting of the syntax-quotes in the first two println calls takes the value of y further up the abstraction ladder. But by including a single unquote in the third println, we again bring it back down. Finally, by unquoting a second time, we’ve created a structure that can then be evaluated—and doing so yields the result -36. We had to use contextual-eval in the tail because core eval doesn’t have access to local bindings—only var bindings. One final note is that had we attempted to unquote one extra time, we’d have seen the exception java.lang.IllegalState-Exception: Var clojure.core/unquote is unbound. The reason for this error is that unquote is the way to “jump” out of a syntax-quote, and to do so more than nesting allows will cause an error. You won’t use this technique in this chapter, and in most cases you won’t need to utilize it unless you’re planning to create macrodefining macros—something you won’t do until section 14.4.3.

In section 2.7, we mentioned quoting and its effects on evaluation, and in this chapter we’ll expand on that theme fully as it relates to Clojure’s macro facility. But the functionality of the quoting forms is orthogonal to macros, and they can be used independently. As we showed in the previous example,[4] using quoting and unquoting in a function allows you to create an evaluation function, contextual-eval, that takes an explicit context map. Rarely will you see the use of syntax-quote outside the body of a macro, but nothing prevents it from being used this way—and doing so is powerful. But the maximum power of quoting forms is fully realized when used with macros.

4 Thanks to George Jahad for the implementation on which contextual-eval is based.

Working from a model where code is data, Clojure is able to manipulate structures into different executable forms at both runtime and compile time. We’ve already shown how you can do this at runtime using eval and contextual-eval, but this doesn’t serve the purpose of compile-time manipulation. It probably doesn’t need saying, but because this is a book about Clojure, we will: macros are the way to achieve this effect.

8.1.2. Macro rules of thumb

Before we begin, we should list a few rules of thumb to observe when writing macros:

  • Don’t write a macro if a function will do. Reserve macros to provide syntactic abstractions or create binding forms.
  • Write an example usage.
  • Expand your example usage by hand.
  • Use macroexpand, macroexpand-1, and clojure.walk/macroexpand-all[5] liberally to understand how your implementation works.

    5 The macroexpand-all function is a useful debugging aid, as we’ll demonstrate in this chapter. But it’s worth knowing that unlike the other macroexpand functions, it doesn’t use exactly the same logic as the Clojure compiler and thus may in some unusual circumstances produce misleading results.

  • Experiment at the REPL.
  • Break complicated macros into smaller functions whenever possible.

Throughout this chapter, you’ll see all these rules to varying degrees. Obviously, we’re trying to balance best practices, teaching, and page counts, so we may not always adhere entirely. Even so, we’ll try to highlight those times when we do break from the recommended heuristics. Having said that, we’ll talk first about the most ubiquitous use of macros: creating custom control structures.

8.2. Defining control structures

Most control structures in Clojure are implemented via macros, so they provide a nice starting point for learning how macros can be useful. Macros can be built with or without using syntax-quote, so we’ll show examples of each.

In languages lacking macros, such as Haskell[6] for example, the definition of control structures relies on the use of higher-order functions such as we showed in section 7.1.2. Although this fact in no way limits the ability to create control structures in Haskell, the approach that Lisps take to the problem is different. The most obvious advantage of macros over higher-order functions is that the former manipulate compile-time forms, transforming them into runtime forms. This allows your programs to be written in ways natural to your problem domain, while still maintaining runtime efficiency. Clojure already provides a rich set of control structures, including but not limited to doseq, while, if, if-let, and do, but in this section you’ll write a few others.

6 Although there’s a GHC extension named Template Haskell that provides a macro-like capability, this isn’t the norm.

8.2.1. Defining control structures without syntax-quote

Because the arguments to defmacro aren’t evaluated before being passed to the macro, they can be viewed as pure data structures and manipulated and analyzed as such. Because of this, amazing things can be done on the raw forms supplied to macros even in the absence of unquoting.

Imagine a macro named do-until that executes all of its clauses evaluating as true until it gets one that is falsey:

(do-until
  (even? 2) (println "Even")
  (odd?  3) (println "Odd")
  (zero? 1) (println "You never see me")
  :lollipop (println "Truthy thing"))
; Even
; Odd
;=> nil

A good example of this type of macro is Clojure’s core macro cond, which with some minor modifications can be made to behave differently:

The first expansion of do-until illustrates how this macro operates:

(macroexpand-1 '(do-until true (prn 1) false (prn 2)))
;=> (clojure.core/when true
  (prn 1) (do-until false (prn 2)))

do-until recursively expands into a series of when calls, which themselves expand into a series of if expressions (because when is a macro defined in terms of the built-in if):

(require '[clojure.walk :as walk])
(walk/macroexpand-all '(do-until true (prn 1) false (prn 2)))
;=> (if true (do (prn 1) (if false (do (prn 2) nil))))

(do-until true (prn 1) false (prn 2))
; 1
;=> nil

You could write out the nested if structure manually and achieve the same result, but the beauty of macros lies in the fact that they can do so on your behalf while presenting a lightweight and intuitive form. In cases where do-until can be used, it removes the need to write and maintain superfluous boilerplate code. This idea can be extended to macros in general and their propensity to reduce unneeded boilerplate for a large category of circumstances, as you desire. One thing to note about do-until is that it’s meant to be used only for side effects, because it’s designed to always return nil. Macros starting with do tend to act the same way.

8.2.2. Defining control structures using syntax-quote and unquoting

Not all control structures are as simple as do-until. Sometimes you’ll want to selectively evaluate macro arguments, structures, or substructures. In this section, we’ll explore one such macro named unless, implemented using unquote and unquote-splice.

Ruby provides a control structure named unless that reverses the sense (Olsen 2007) of a when statement, executing the body of a block when a given condition evaluates to false:

(unless (even? 3) "Now we see it...")
;=> "Now we see it..."

(unless (even? 2) "Now we don't.")
;=> nil

The maverick implementation[7] of unless as demonstrated previously and as shown next is straightforward:

7 The proper way to define unless is either (defmacro unless [& args] `(when-not ~@args)) or (clojure.contrib.def/defalias unless when-not)—or just use when-not from the start.

The body of the unless implementation uses syntax-quote, unquote, and unquote-splice. Syntax-quote allows the if form to act as a template for the expression that any use of the macro becomes when expanded. The unquote and splicing-unquote provide the “blanks” where the values for the parameters condition and body will be inserted. You can see unless in action next:

(unless true (println "nope"))
;;=> nil

(unless false (println "yep!"))
;; yep!
;;=> nil

Because unless relies on the result of a condition for its operation, it’s imperative that it evaluate the condition part using unquote. If we didn’t use unquote in our example, then instead of evaluating a function (even? 3), it would attempt to resolve a namespace var named condition that may not exist—and if it did exist, it might be arbitrarily truthy at the time of the macro call. Some of the unintended consequences of this mistake are shown here:

Clearly this isn’t the desired behavior. Instead, by unquoting the condition local, you ensure that the function call is used instead. It’s easy to forget to add an unquote to the body of a macro, and depending on the condition of your runtime environment, the problem may not be immediately obvious.

8.3. Macros combining forms

Macros are often used for combining a number of forms and actions into one consistent view. You saw this behavior in the previous section with the do-until macro, but it’s more general. In this section, we’ll show how you can use macros to combine a number of tasks in order to simplify an API. Clojure’s defn macro is an instance of this type of macro because it aggregates the processes of creating a function, including the following:

  • Creating the corresponding function object using fn
  • Checking for and attaching a documentation string
  • Building the :arglists metadata
  • Binding the function name to a var
  • Attaching the collected metadata

You could perform all these steps over and over again every time you wanted to create a new function, but thanks to macros you can instead use the more convenient defn form. Regardless of your application domain and its implementation, programming language boilerplate code inevitably occurs and is a fertile place to hide subtle errors. But identifying these repetitive tasks and writing macros to simplify and reduce or eliminate the tedious copy-paste-tweak cycle can work to reduce the incidental complexities inherent in a project. Where macros differ from techniques familiar to proponents of Java’s object-oriented style—including hierarchies, frameworks, inversion of control, and the like—is that they’re treated no differently by the language itself. Clojure macros work to mold the language into the problem space rather than force you to mold the problem space into the constructs of the language. There’s a specific term for this, domain-specific language, but in Lisp the distinction between DSL and API is thin to the point of transparency.

Envision a scenario where you want to be able to define vars that call a function whenever their root bindings change. You could do this using the add-watch function, which lets you attach a watcher to a reference type that’s called whenever a change occurs within. The add-watch function takes three arguments: a reference, a watch function key, and a watch function called whenever a change occurs. You could enforce that every time someone wanted to define a new var, they would have to follow these steps:

1.  Define the var.

2.  Define a function (maybe inline to save a step) that will be the watcher.

3.  Call add-watch with the proper values.

A meager three steps isn’t too cumbersome a task to remember in a handful of uses, but over the course of a large project it’s easy to forget and/or morph one of these steps when the need to perform them many times occurs. Therefore, perhaps a better approach is to define a macro to perform all these steps for you, as the following definition does:

(defmacro def-watched [name & value]
  `(do
     (def ~name ~@value)
     (add-watch (var ~name)
                :re-bind
                (fn [~'key ~'r old# new#]
                  (println old# " -> " new#)))))

Ignoring symbol resolution and auto-gensym, which we’ll cover in upcoming sections, the macro called as (def-watched x 2) expands into roughly the following:

(do (def x 2)
    (add-watch (var x)
               :re-bind

               (fn [key r old new]
                 (println old " -> " new))))

The results of def-watched are thus

(def-watched x (* 12 12))
x
;=> 144

(def x 0)
; 144 -> 0

Lisp programs in general (and Clojure programs specifically) use macros of this sort to vastly reduce the boilerplate needed to perform common tasks. Throughout this chapter, you’ll see macros that combine forms, so there’s no need to dwell on the matter here. Instead, we’ll move on to a macro domain that does just that, with the added bonus of performing some interesting transformations in the process.

8.4. Using macros to change forms

One way to design macros is to start by writing out example code that you wish worked—code that has the minimal distance between what you must specify and the specific application domain in which you’re working. Then, with the goal of making this code work, you begin writing macros and functions to fill in the missing pieces.

For example, when designing software systems, it’s often useful to identify the “things” that make up your given application domain, including their logical groupings. The level of abstraction at this point in the design is best kept high (Rosenberg 2005) and shouldn’t include details about implementation. Imagine that you want to describe a simple domain of the ongoing struggle between humans and monsters:

  • Man versus monster

    • People

      • Men (humans)

        • Name
        • Have beards?
    • Monsters

      • Chupacabra

        • Eats goats?

Although this is a simple format, it needs work to be programmatically useful. Therefore, the goal of this section is to write macros that perform the steps to get from this simple representation to the one more conducive to processing. The outline form can be rendered in a Clojure form, assuming the existence of some macros and functions you’ve yet to define, like this:

(domain man-vs-monster
  (grouping people
    (Human "A stock human")

    (Man (isa Human)

      "A man, baby"
      [name]
      [has-beard?]))
  (grouping monsters
    (Chupacabra
      "A fierce, yet elusive creature"
      [eats-goats?])))

One possible structure underlying this sample format is a tree composed of individual generic nodes, each taking a form similar to the following:

You’d never say this is a beautiful format, but it does present practical advantages over the original format—it’s a tree, it’s composed of simple types, it’s regular, and it’s recognizable to some existing libraries.

Clojure Aphorism

Clojure is a design language where the conceptual model is also Clojure.

Start with the outer-level element, domain:

(defmacro domain [name & body]
  `{:tag :domain,
    :attrs {:name (str '~name)},
    :content [~@body]})

The body of domain is fairly straightforward in that it sets the domain-level tree node and splices the body of the macro into the :content slot. After domain expands, you’d expect its body to be composed of a number of grouping forms, which are then handled by an aptly named macro:

(declare handle-things)

(defmacro grouping [name & body]
  `{:tag :grouping,
    :attrs {:name (str '~name)},
    :content [~@(handle-things body)]})

Similar to domain, grouping expands into a node with its body spliced into the :content slot. In its body, the grouping macro uses a form named handle-things that hasn’t been written yet, so you have to use declare to avoid a compilation error. But grouping differs from domain in that it splices in the result of the call to a function, handle-things:

(declare grok-attrs grok-props)

(defn handle-things [things]
  (for [t things]
    {:tag :thing,
     :attrs (grok-attrs (take-while (comp not vector?) t))
     :content (if-let [c (grok-props (drop-while (comp not vector?) t))]

                [c]
                [])}))

Because the body of a thing is fairly simple and regular, you can simplify the implementation of handle-things by again splitting it into two functions. The first function, grok-attrs, handles everything in the body of a thing that’s not a vector, and grok-props handles properties that are. In both cases, these leaf-level functions return specifically formed maps:

(defn grok-attrs [attrs]
  (into {:name (str (first attrs))}
        (for [a (rest attrs)]
          (cond
            (list? a) [:isa (str (second a))]
            (string? a) [:comment a]))))

The implementation of grok-attrs may seem overly complex, especially given that the sample domain model DSL only allows for a comment attribute and an optional isa specification (as shown in the sample layout in the beginning of this section). But by laying it out this way, you can easily expand the function to handle a richer set of attributes later. Likewise with grok-props, this more complicated function pulls apart the vector representing a property so it’s more conducive to expansion:

(defn grok-props [props]
  (when props
    {:tag :properties, :attrs nil,
     :content (apply vector (for [p props]
                 {:tag :property,
                  :attrs {:name (str (first p))},
                  :content nil}))}))

Now that you’ve created the pieces, take a look at the new DSL in action:

You can navigate this structure as follows:

(:tag d)
;=> :domain

(:tag (first (:content d)))
;=> :grouping

Maybe that’s enough to prove to you that you’ve constructed the promised tree, but probably not. Therefore, you can pass a tree into a function that expects one of that form[8] and see what comes out on the other end:

8 The namespace clojure.contrib.json in the Clojure contrib library also contains some functions that can handle the domain DSL structure seamlessly. Additionally, Enlive (http://mng.bz/8Hh6) should also recognize the resultant structure.

(use '[clojure.xml :as xml])
(xml/emit d)

Performing this function call prints out the corresponding XML representation, minus the pretty printing:

Our approach was to define a single macro entry point domain, intended to build the top-level layers of the output data structure and instead pass the remainder on to auxiliary functions for further processing. In this way, the body of the macro expands into a series of function calls, each taking some subset of the remaining structure and returning some result that’s spliced into the final result. This functional composition approach is fairly common when defining macros. The entirety of the domain description could have been written in one monolithic macro, but by splitting the responsibilities, you can more easily extend the representations for the constituent parts.

Macros take data and return data, always. It so happens that in Clojure, code is data and data is code.

8.5. Using macros to control symbolic resolution time

Whereas functions accept and return values that are meaningful to an application at runtime, macros accept and return code forms that are meaningful at compile time. Any symbol has subtleties depending on whether it’s fully qualified, its resolution time, and its lexical context. These factors can be controlled in any particular case by the appropriate use of quoting and unquoting, which we explore in this section.

The term name capture refers to a potential problem in macro systems where a name generated at compile time clashes with a name that exists at runtime. Clojure macros are mostly safe from name capture, because the use of syntax-quote attempts to resolve symbols at macro-expansion time. This strategy reduces complexity by ensuring that symbols refer to those available at macro-expansion time, rather than to those unknown in the execution context.

For example, consider one of the simplest possible macros:

(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 because it helps to avoid problems with free name capturing that are possible in a macro system such as that found in Common Lisp.[9] Here’s an example that would trip up a lesser implementation of syntax-quote, but which does just what you want in Clojure:

9 One of the ways 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.

(def x 9)
(let [x 109] (resolution))
;=> 9

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 throw an unbound var exception if you didn’t first execute the call to def. If it didn’t error out this way, the local version of x would be used instead, which might not be what you intend.

Clojure does provide a way to defer symbolic resolution for those instances where it may be useful to resolve it in the execution context, which we’ll show now.

8.5.1. Anaphora

Anaphora[10] in spoken language is a term used in a sentence that refers back to a previously identified subject or object. It helps to reduce repetition in a phrase by replacing “Jim bought 6,000 Christmas lights and hung all 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 definition. Scala has a rich set of anaphoric (Odersky 2008) patterns primarily focused around its _ operator:

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

// NOTE: This is Scala, not Clojure

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 generally aren’t employed in Clojure. Within a nested structure of anaphoric macros, you can only refer to the most immediate anaphoric binding, and never those from outer lexical contours, as demonstrated in the next example. For example, the Arc programming language (Graham Arc) contains a macro named awhen that is similar to Clojure’s when, except that it implicitly defines a local named it used in its body to refer to the value of the checked expression. You can implement the same macro, called awhen in Clojure, as shown here:

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 preferable to 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 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 for selectively capturing a symbolic name in the body of a macro. The reason for this bit of awkwardness[11] is that Clojure’s syntax-quote attempts to resolve symbols in the current context, resulting in fully qualified symbols. Therefore, ~' avoids that resolution by unquoting a quote.

11 According to Christophe Grand (in a personal communication), “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.”

8.5.2. (Arguably) useful selective name capturing

We contend that there’s only one case to be made for selective name capturing in Clojure 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 in its body for use therein. We’ll cover the proxy macro in depth in section 12.1, so there’s no need to discuss it here. But bear in mind that should this macro ever be embedded in 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 in the body of syntax-quote at macro-definition time. As you saw, symbols are expanded into the form user/a-symbol in the body of syntax-quote. To close this hygienic loop, Clojure also disallows the definition of qualified locals in the body of a macro. In order to selectively capture names in 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:[12]

12 Java 7 introduces an AutoCloseable interface coupled with a try-with-resources statement that operates almost exactly like the with-resource macro. See “The try-with-resources Statement,” The Java Tutorials, http://mng.bz/389y.

// NOTE: This is Java, not Clojure
try {

     // open the resource
}
catch (Exception e) {
     // handle any errors
}
finally {
// in any case, release the resource
}

We showed in section 2.9 that Clojure also has a try/catch/finally form that can be used the same way, but as with the Java idiom, you must remember to explicitly close the resource in the finally block. Clojure provides a generic with-open macro, shown next, that when given a closeable object bound to a name, automatically calls its .close method (assuming that one exists) in a finally block:

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:

(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 differing flavors (Symbolics Inc.)[13] to almost be considered a Lisp design pattern. The macro with-resource differs from with-open in that it doesn’t 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 à 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 in Clojure and has been adopted by most codebases in the wild.

13 The spirit of this section was inspired by a similar discussion of “Writing Macros to Surround Code” (“Reference Guide to Symbolics Common Lisp: Language Concepts,” Symbolics Release 7 Document Set, 1986). If you can get your hands on the original Symbolics manuals, do so—they contain a wealth of information.

8.7. Putting it all together: macros returning functions

In section 7.1, we introduced Clojure’s constraint facility that uses pre- and postcondition checks on function arguments and return values, respectively, to ensure some assertions about said function. In that section, we talked briefly about how separating the constraints from the functions they’re constraining allows you to more flexibly 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’ll 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 postconditions 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 the preconditions, and the :ensure list refers to 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 let’s start there.

Listing 8.1. Contract top-level macro
(declare collect-bodies)
(defmacro contract [name & forms]
  (list* `fn name (collect-bodies forms)))

Hold fast, because you’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:

(fn doubler
  ([f x]
     {:post [(= (* 2 x) %)],
      :pre [(pos? x)]}
     (f x)))

You also want to allow for the multi-arity function definition form so that the contract 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 you’ll then pass along to another function named build-contract, which will build the arity bodies and corresponding constraint maps. This is shown next.

Listing 8.2. Contract auxiliary function build-contract

The function build-contract is where the heart of contract construction lies, building 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, to which the arguments are then delegated. This allows you to compose the contract function with a constrained function, as shown in the next listing.

Listing 8.3. Composition of the contract function and constrained function

As you might expect, times2 fulfills the contract, whereas times3 doesn’t. You could extend doubler-contract to handle extended arities, as shown here.

Listing 8.4. Contract for multiple-arity functions

You 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, you provide 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 can 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 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. We also tried to show how you can use macros 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 mastery 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 ways to organize and categorize data types and functions using Clojure’s namespaces, multimethods, types, and protocols.

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

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