Macros from within Clojure

Một phần của tài liệu Manning clojure in action 2nd (Trang 200 - 203)

In this section, we’ll look at some macros. Many come from the source code of the Clojure language itself; some are from elsewhere. These examples should give you a flavor of macro style and ideas about how to use macros in your own code.

Let’s begin our journey with examples of macros from the Clojure language itself. As mentioned in the previous section, much of Clojure is implemented in Clo- jure itself, and a lot of that code is macros. This allows the core language to remain small; Clojure has only about a dozen special forms. This approach allows most other features of the language to be developed in Clojure itself. We’ll examine a few macros now.

7.2.1 comment

The comment macro is a great one to start with because it’s so simple. It literally does nothing; this is an example of ignoring code altogether, as opposed to changing the flow of execution or delaying it. This macro allows you to comment out parts of your program or to add comments to your code.

Here’s the complete implementation:

(defmacro comment [& body])

The comment macro returns nil. 7.2.2 declare

Here’s a macro that does a little bit more. The declare macro accepts one or more symbols to let Clojure know that there may be references to them in the code that fol- lows. The macro goes through each argument and creates a var with that name. Typi- cally, these vars are redefined at a later point in the program.

A slightly simplified version of how the declare macro is implemented follows:

(defmacro declare [& names]

`(do

~@(map #(list 'def %) names)))

You can see how it works by using the macroexpand function:

(macroexpand '(declare add multiply subtract divide))

;=> (do

(def add) (def multiply) (def subtract) (def divide))

The formatting isn’t part of the macro expansion. It’s just a simple way to get rid of duplication from having to define multiple vars. You couldn’t accomplish this with a function, by the way, because def is a special form that accepts only a symbol. Inside of macros, all special forms become available because we’re operating at the s-expression (or symbolic) level. This is an important advantage of macros.

7.2.3 defonce

We’ll now look at a macro that evaluates conditional expressions. defonce is a macro that accepts the name of a var and an initialization expression. But if the var has already been initialized once (has a root binding), it won’t be reinitialized. The imple- mentation of this macro is straightforward, so we don’t even need to use macro expan- sion to see what’s going on:

(defmacro defonce [name expr]

`(let [v# (def ~name)]

(when-not (.hasRoot v#) (def ~name ~expr))))

Notice the unquoting going on here in the last line—it’s what substitutes the name of the var in the def form, as well as the expression being passed in.

A def isn’t normally evaluated more than once, so you may be wondering why defonce exists. When using an editor with an integrated REPL, it’s common to reeval- uate an entire namespace to refresh the REPL environment with updates from the edi- tor: this means that any defs will be reevaluated, even if they take a long time to evaluate or contain a stateful resource such as a database connection. defonce is used to avoid this problem.

7.2.4 and

Let’s now look at a slightly more complex example. In most languages and as well as other logical operators are implemented as special forms. In other words, they’re built into the core of the language. In Clojure, and is just another macro:

(defmacro and ([] true) ([x] x)

179 Macros from within Clojure

([x & next]

`(let [and# ~x]

(if and# (and ~@next) and#))))

This is an elegant piece of code! When and is called with no arguments, it returns true. When it’s called with a single argument, the return value is the argument itself (remember that anything other than nil or false is treated as true). When there are multiple arguments, the macro evaluates the first argument. It then tests it with the if form. If the value is logically true, the macro calls itself with the remaining argu- ments. The process then repeats. If the evaluation of any argument returns a logical false, the if form returns that value as is.

You can use macroexpand to see what happens:

(macroexpand '(and (even? x) (> x 50) (< x 500)))

;=> (let* [and__4357__auto__ (even? x)]

(if and__4357__auto__

(clojure.core/and (> x 50) (< x 500)) and__4357__auto__))

You may see something slightly different because the auto-gensym will create differ- ent names for the local symbols. Also, remember that macroexpand doesn’t expand macros contained in subexpressions. In reality, the macro will be completely expanded, and the final expanded s-expression will replace the original call to and.

7.2.5 time

time is a rather handy macro, useful for quick checks on how slow or fast your code is running. It accepts an expression, executes it, prints the time it took to execute, and then returns the result of the evaluation. Here’s an example:

(time (* 1331 13531))

"Elapsed time: 0.04 msecs"

;=> 18009761

Using the time macro isn’t as sophisticated as using a profiler, for instance, but it can be quite useful for quick benchmarks of your code. Here’s how it’s implemented:

(defmacro time [expr]

`(let [start# (. System (nanoTime)) ret# ~expr]

(prn

(str "Elapsed time: "

(/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))

ret#))

As you can see, the macro starts a timer before evaluating the expression passed in.

The value of the expression is captured and returned after the timer is stopped and the duration printed to the console.

These are just a few macros that can be found in Clojure’s source code. As men- tioned earlier, it’s advantageous for a language to have a small core and have all other

features built on top of it using regular code. Clojure does this in an elegant fashion, and reading through the source code is a great way to learn the tricks of the trade.

You’ll now write some macros of your own.

Một phần của tài liệu Manning clojure in action 2nd (Trang 200 - 203)

Tải bản đầy đủ (PDF)

(338 trang)