Slide 1

Slide 1 text

Writing Clojure Macros! Kurt Christensen!

Slide 2

Slide 2 text

Who is this guy?! What is he talking about?! Why do I care?!

Slide 3

Slide 3 text

Who IS this guy?! Kurt Christensen! Programmer! Software development coach! for small teams, big corporations,! and everything in between! Available for purchase at:! [email protected]! Twitter: @projectileboy!

Slide 4

Slide 4 text

(Lisp)! John McCarthy, creator of Lisp! http:/ /www-formal.stanford.edu/jmc!

Slide 5

Slide 5 text

What got me started…! "...I think I can give a kind of argument that might be convincing. The source code of the Viaweb editor was probably about 20-25% macros. Macros are harder to write than ordinary Lisp functions, and it's considered to be bad style to use them when they're not necessary. So every macro in that code is there because it has to be. What that means is that at least 20-25% of the code in this program is doing things that you can't easily do in any other language. However skeptical the Blub programmer might be about my claims for the mysterious powers of Lisp, this ought to make him curious. We weren't writing this code for our own amusement. We were a tiny startup, programming as hard as we could in order to put technical barriers between us and our competitors.” Paul Graham, Beating the Averages

Slide 6

Slide 6 text

What’s so special about Lisp, and macros?! Language enables us to define and combine! increasingly powerful abstractions:! Data abstractions! Functional abstractions! Syntactic abstractions! Macros enable syntactic abstractions! (you can define your own special forms)!

Slide 7

Slide 7 text

So then what is Clojure?! Simply, Clojure is a dialect of Lisp! which runs on the JVM and the CLR! ⇒  Lisp we can actually use!

Slide 8

Slide 8 text

Getting started with Clojure! If you just want to try, go to http://try-clojure.org! Or…! 1)  Check that your machine has Java! 2)  Download the latest Clojure from http://clojure.org! 3)  Open a REPL (Read-Eval-Print Loop):! > java -jar clojure.jar Clojure 1.2.0 user=> 4) At the REPL prompt, begin to play:! user=> (+ 2 2) 4

Slide 9

Slide 9 text

What does Clojure look like?! We’ll experiment in the REPL…! user=> (defn hello [name] (prn (str "Hello, " name))) #'user/hello user=> (hello ”Twin Cities Code Camp") "Hello, Twin Cities Code Camp” nil Note that prn prints out a string as a side effect,! and returns nil. Clojure guides you towards! functional programming.!

Slide 10

Slide 10 text

Clojure Data Structures! The usual suspects, with some nice syntactic sugar! for generating them…! user=> (def my-vector [1 2 3 4 5 ]) #'user/my-vector user=> (def my-list '(1 2 3 4)) #'user/my-list user=> (def my-set #{1 2 3 4 }) #'user/my-set user=> (def my-map {:x 10 :y 20 }) #'user/my-map

Slide 11

Slide 11 text

Clojure Functions! As in other functional programming languages (C#, etc.) functions are first-class citizens in Clojure! user=> (def hello (fn [name] (prn (str "Hello, " name)))) #'user/hello user=> (def hello #(prn (str "Hello, " %))) #'user/hello user=> (defn greet [f & names] (apply f names)) #'user/greet user=> (greet hello "Kurt") "Hello, Kurt" nil

Slide 12

Slide 12 text

Functional Programming! With first-class functions, sequences (lists, etc.)! can be manipulated in powerful ways...! user=> (sort '(77 22 33 11 99 88)) (11 22 33 77 88 99) user=> (map #(+ 1 %) '(1 2 3 4 5)) (2 3 4 5 6) user=> (filter #(> % 50) '(10 20 60 70)) (60 70) user=> (reduce + '(1 2 3 4 5)) 15

Slide 13

Slide 13 text

…and much, much more!! •  Seamless interoperability with Java! •  The sequence abstraction! •  Concurrency constructs! •  Software transactional memory! •  Multimethods! …but we’re here to talk about macros!!

Slide 14

Slide 14 text

How do macros work?! In Lisp, everything is an expression ! - including functions – ! which gets evaluated at runtime! EXCEPT…! Macros are evaluated at compile time,! returning expressions which are ! in turn evaluated at runtime!

Slide 15

Slide 15 text

How do macros work?! Isn’t this the same as a C/C++ macro?! No.! C/C++ macros perform string substitution,! Lisp macros are functions evaluated at compile-time! So?! Lisp macros have:! (a)  Lexical scope! (b)  Logic!

Slide 16

Slide 16 text

How do macros work?! Other languages have meta-programming ! and code generation facilities! But only in Lisp are functions themselves ! also Lisp data structures! Lisp programs are their own abstract syntax trees! ⇒  We can use Lisp to make Lisp!

Slide 17

Slide 17 text

The Simplest Macro! user=> (defmacro rev [x y z] (list z y x)) #'user/rev user=> (rev 2 1 +) 3

Slide 18

Slide 18 text

What just happened?! We can use macroexpand to see ! the code generated by the macro! user=> (macroexpand-1 '(rev 2 1 +)) (+ 1 2) user=> (macroexpand '(rev 2 1 +)) (+ 1 2) macroexpand recursively expands all macros! Normally you only want to see what your macro is doing; for that you call macroexpand-1!

Slide 19

Slide 19 text

Gee, it sure would be nice if we had! some sort of templating language…! user=> (def x '(1 2 3)) #'user/x user=> `(0 x 4) ;Backquote shuts off evaluation (0 user/x 4) user=> `(0 ~x 4) ;Tilde evaluates and inserts (0 (1 2 3) 4) user=> `(0 ~@x 4) ;Tilde-at evaluates and splices (0 1 2 3 4)

Slide 20

Slide 20 text

The Simplest Macro, One More Time! user=> (defmacro rev [x y z] `(~z ~y ~x)) #'user/rev user=> (rev 2 1 +) 3 Note to Lispers: Clojure does not have read macros!!

Slide 21

Slide 21 text

Why do I care?! “I’ve never needed macros!”! …are you sure about that?! “Programming languages teach you not to want what they cannot provide. You have to think in a language to write programs in it, and it’s hard to want something you can’t describe.” Paul Graham, ANSI Common Lisp

Slide 22

Slide 22 text

Look familiar?! How do we deal with expensive log messages?! user=> (def log-level :debug) #'user/log-level user=> (defn log [msg] (if (= log-level :debug) (prn msg))) #'user/log user=> (defn make-expensive-msg [] (prn "Generating expensive log message”) "Expensive log message") #'user/make-expensive-msg user=> (def log-level :prod) #'user/log-level user=> (log (make-expensive-msg)) "Generating expensive log message” nil

Slide 23

Slide 23 text

Macros to the rescue!! user=> (defmacro log [msg] `(if (= log-level :debug) (prn ~msg))) #'user/log user=> (log (make-expensive-msg)) nil user=> (def log-level :debug) #'user/log-level user=> (log (make-expensive-msg)) "Generating expensive log message" "Expensive log message" nil

Slide 24

Slide 24 text

Note that we refactored towards a macro,! working backwards from the code! we wanted to generate!

Slide 25

Slide 25 text

The problem of variable capture! Compile-time sets different traps than run-time…! user=> (defmacro rep [n & body] `(dotimes [i ~n] ~@body)) #'user/rep user=> (let [i 100] (rep 3 (prn i))) java.lang.Exception: Can't let qualified name: user/i (NO_SOURCE_FILE:2) …WTF?!?! user=> (macroexpand-1 '(rep 3 (prn i))) (clojure.core/dotimes [user/i 3] (prn i)) Oh…!

Slide 26

Slide 26 text

The solution: Gensyms! (that is, generated symbols)! Essentially what we want is for the system! to generate a symbol for us that is guaranteed! not to conflict with any other symbols!

Slide 27

Slide 27 text

Clojure auto-gensyms! Clojure has a nifty little syntax for doing this:! user=> (defmacro rep [n & body] `(dotimes [i# ~n] ~@body)) #'user/rep user=> (let [i 100] (rep 3 (prn i))) 100 100 100 nil user=> (macroexpand-1 '(rep 3 (prn i))) (clojure.core/dotimes [i__9__auto__ 3] (prn i))

Slide 28

Slide 28 text

Icky? Perhaps, but…! Just as you learn to deal with certain types! of runtime problems, with macros you must learn! to deal with certain types of compile time problems! (see chapter 10 of On Lisp)!

Slide 29

Slide 29 text

Some languages try to avoid all of this! with the notion of hygienic macros,! which prevent unwanted variable capture! This is probably more trouble than it’s worth! More importantly, they also prevent ! wanted variable capture!

Slide 30

Slide 30 text

Intentional variable capture! Macros which create variables that refer to their own internal state are called anaphoric macros! user=> (defmacro aif [pred & body] `(let [~'it ~pred] (if ~'it (do ~@body)))) #'user/aif user=> (macroexpand '(aif "Kurt" (prn it))) (let* [it "Kurt"] (clojure.core/if it (do (prn it)))) user=> (aif "Kurt" (prn it)) "Kurt” nil user=> (aif nil (prn "Kurt")) nil

Slide 31

Slide 31 text

Intentional variable capture! A bit of trivia for any Lispers in the house…! user=> `(0 x 4) (0 user/x 4) user=> `(0 ~'x 4) (0 x 4) Note that Clojure wants to express fully namespace- qualified symbols. That’s why we need the weird syntax to do anaphoric macros in Clojure. !

Slide 32

Slide 32 text

A Note About the Clojure Community…! Community is important for languages! The Clojure community discourages anaphoric macros, and seems to fear macros in general! Good thing: The Clojure community does not carry the baggage of the Common Lisp community! Bad thing: The Clojure community does not carry the wisdom of the Common Lisp community!

Slide 33

Slide 33 text

Applied Macros! Macros enable one to define language.! The possibilities are endless!! Common macro uses:! Defining new syntacic abstractions that can’t be encaspulated in functions due to special form behavior! Increasing performance by executing code! at compile-time instead of at run-time !

Slide 34

Slide 34 text

Applied Macros! A (weak) example: Kurt tries to generate HTML! …and remember that macroexpand-1 is your friend!!

Slide 35

Slide 35 text

"One of the things you'll discover as you learn more about macros is how much day-to-day coding in other languages consists of manually generating macroexpansions... When there are patterns in source code, the response should not be to enshrine them in a list of ‘best practices’, or to find an IDE that can generate them. Patterns in your code mean you're doing something wrong. You should write the macro that will generate them and call that instead.” Paul Graham, Arc Tutorial

Slide 36

Slide 36 text

What do you value?! I value simplicity over flexibility! I value few parts as a manifestation of simplicity! I value a complex part which! eliminates a large number of other parts! You may value different things…!

Slide 37

Slide 37 text

References! http://clojure.org! http://try-clojure.org! The Joy of Clojure! Michael Fogus and Chris Houser! http://joyofclojure.com!

Slide 38

Slide 38 text

References! Suggestion: First learn Lisp, and then worry! about the specifics of Clojure! ANSI Common Lisp! Paul Graham! http://www.amazon.com/dp/0133708756! Practical Common Lisp! Peter Seibel! http://gigamonkeys.com/book!

Slide 39

Slide 39 text

References! On Lisp! Paul Graham! http://www.paulgraham.com/onlisptext.html! Let Over Lambda! Doug Hoyte! http://letoverlambda.com! Structure and Interpretation of Computer Programs! Abelman and Sussman! http://mitpress.mit.edu/sicp!

Slide 40

Slide 40 text

Tools! Many prefer emacs…! Or TextMate (on the Mac)! IntelliJ IDEA with La Clojure plugin! Comprehensive categorized listing at! http://www.clojure-toolbox.com! Clojure should work fine on .NET, but there’s no tooling –! anyone care to write a Visual Studio plugin…?!

Slide 41

Slide 41 text

Questions?! The fonts used in this presentation were Chalkboard, Liberation Mono, and Gill Sans Light.! No characters were harmed during the making of this presentation.!