Clojure Macros Macros defined n Metaprogramming is writing

  • Slides: 9
Download presentation
Clojure Macros

Clojure Macros

Macros, defined n Metaprogramming is writing code that produces code n Metaprogramming is particularly

Macros, defined n Metaprogramming is writing code that produces code n Metaprogramming is particularly easy in the Lisp family of languages, because of homoiconicity n n n “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 n n n Arguments to a macro are not evaluated Macro calls are evaluated at compile time The return value of a macro should be executable code 2

A trivial macro: triple-do n (defmacro triple-do [form] (list 'do form) ) n n

A trivial macro: triple-do n (defmacro triple-do [form] (list 'do form) ) n n n 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")) n n Result: The list (do (println "Hello")) If executed from the REPL, the result is Hello nil 3

Quotes and unquotes n n n The usual way of quoting something is to

Quotes and unquotes n n n 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 n n Quotes and unquotes can be nested, to any level The following are equivalent: n n (defmacro triple-do [form] (list 'do form) ) (defmacro triple-do [form] `(do ~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 4

Splicing unquotes n n n println can take multiple arguments, which it prints in

Splicing unquotes n n n 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 n n (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 n (defmacro rev-println [args] `(println ~@(reverse args)) ) 5

Generating symbols n Just as in a function, you can use let to define

Generating symbols n Just as in a function, you can use let to define the names of local variables n n n 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 n n Clojure will replace these names with unique, generated names Example: A println macro that also returns the value printed n (defmacro debug-println [expr] `(let [result# ~expr] (println (str “Value is: “ result#)) result# ) ) 6

Debugging n n To see what you get as the result of expanding a

Debugging n n To see what you get as the result of expanding a macro, use macroexpand Example: (macroexpand '(triple-do (println "Hello"))) gives (do (println "Hello")) 7

When to use macros n General rule: If you can do it with a

When to use macros n General rule: If you can do it with a function, do it with a function n n 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 8

The End 9

The End 9