Transcript Clojure 7

Clojure Macros
Homoiconicity

All versions of Lisp, including Clojure, are homoiconic


Original Lisp used only lists and atoms


“atom”: A simple value (number, string, nil)
Clojure adds vectors, maps, and a few other things



This means that there is no difference between the form of the
data and the form of the program
The result is still homoiconic, even if it’s less obviously true
The compiler mostly compiles everything down into a
common form anyway
Homoiconicity greatly simplifies metaprogramming
2
Macros, defined

Metaprogramming is writing code that produces code

Metaprogramming is particularly easy in the Lisp family of
languages, because of homoiconicity



“All code is data, and all data is code”
In Lisp languages, macros are the primary means of
doing metaprogramming
A macro definition is like a function definition, with
three important differences



Arguments to a macro are not evaluated
Macro calls are evaluated at compile time
The return value of a macro should be executable code
3
A trivial macro: triple-do

(defmacro triple-do [form]
(list 'do form form form) )



The do is quoted, so it is put as is into the result list
Three copies of the value passed in to the form are put into the list
(triple-do (println "Hello"))


Result: The list
(do (println "Hello")
(println "Hello")
(println "Hello"))
If executed from the REPL, the result is
Hello
Hello
Hello
nil
4
Why macros?

When you write any reasonably sized program, you are
designing a language to solve a particular class of
problems




Your functions are the “verbs” in the language
Your variables are the “nouns” in the language
Your “grammar” is imposed by the language—if statements,
while loops, and so on
Metaprogramming allows you to define new “grammar”
for your language
5
Abstract Syntax Trees


For virtually every programming language, a compiler or
interpreter parses programs into an abstract syntax tree
In Lisp, the AST is represented directly!
(if (> x y) (reset! max x) (reset! max y))
6
Syntax

In a sense, Lisp (and Clojure) are “syntax free”




There is syntax, but it directly represents the abstract syntax
tree
You can define new functions, but you cannot define new
special forms
Macros give you a power equivalent to writing new
special forms
Because of homoiconicity, Lisp macros are far more
powerful that “macros” in C
7
Quotes and unquotes



The usual way of quoting something is to put a single quote mark
in front of it: '(a b c)
The backquote does the exact same thing: `(a b c)
However, things within a backquote can be “unquoted” and
evaluated, by putting a tilde, ~, in front of them


Quotes and unquotes can be nested, to any level
The following are equivalent:




(defmacro triple-do [form]
(list 'do form form form) )
(defmacro triple-do [form]
`(do ~form ~form ~form) )
The second of these is called a “template”
For complex macros, templates can be a lot easier to read, because they
“look like” the code that is generated
8
Splicing unquotes



println can take multiple arguments, which it prints in order
Suppose you wanted to write a macro that prints its arguments
in reverse order
You might try




(defmacro rev-println [args]
`(println ~(reverse args)) )
Given a list of values, this will print them as a list
(rev-println `(a b c)) would print (c b a), not c b a
The splicing-unquote operator, ~@, will insert the list
values individually, not as a list

(defmacro rev-println [args]
`(println ~@(reverse args)) )
9
Generating symbols

Just as in a function, you can use let to define the names of local
variables



These names can be used in the generated code, where they will appear as
written
However, the names in the generated code may conflict with other names
in use where the macro is called
To avoid this issue: in any syntax-quoted form (forms using the
back tick), add the # symbol to the end of any local names


Clojure will replace these names with unique, generated names
Example: A println macro that also returns the value printed

(defmacro debug-println [expr]
`(let [result# ~expr]
(println (str “Value is: “ result#))
result# ) )
10
Debugging


To see what you get as the result of expanding a macro, use
macroexpand
Example:
(macroexpand '(triple-do (println "Hello")))
gives
(do (println "Hello") (println "Hello")
(println "Hello"))
11
Why not macros?



Macros are a higher level of abstraction that the usual
features of a programming language
This makes them more powerful, but also harder to
reason about
Don’t kill flies with sledgehammers!
12
When to use macros

General rule: If you can do it with a function, do it
with a function


Macros are easy to describe but difficult to reason about
The primary thing that macros do for you is allow you
to modify the language by creating new control
structures and formalizing recurring patterns
13
The End
14