Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013

Preview:

DESCRIPTION

Slides used for the exercises in the Macros hands-on at LambdaJam 2013 in Brisbane, Australia and CUFP 2013 in Boston, USA.

Citation preview

Bending Clojure to your Will: Macros and Domain Specific

LanguagesCUFP - Boston, 2013

Leonardo Borges@leonardo_borgeswww.leonardoborges.comwww.thoughtworks.com

Thursday, 19 September 13

Leonardo Borges@leonardo_borgeswww.leonardoborges.comwww.thoughtworks.com

• Thoughtworker• Functional Programming enthusiast• Clojure Evangelist• Founder & Organiser of the Sydney Clojure User Group (clj-syd)• World traveller• Fan of Murray’s Beers :)

about:me

Thursday, 19 September 13

Why macros?

Thursday, 19 September 13

It’s fun

Thursday, 19 September 13

Thursday, 19 September 13

It’s powerful

Thursday, 19 September 13

Thursday, 19 September 13

It’s mind bending

Thursday, 19 September 13

Thursday, 19 September 13

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

- Guy Steele

Thursday, 19 September 13

But what are macros?

Thursday, 19 September 13

•Data is code is data•Programs that write programs•Magic happens at macro-expansion time•Most control structures in Clojure are built out of macros

But what are macros?

Thursday, 19 September 13

Macro-expansion time

(defmacro arg-logger [& args] (prn "Called with: " args) `(do ~@args))

Thursday, 19 September 13

Macro-expansion time

(arg-logger (+ 2 3))

Every usage of a macro

(do (+ 2 3))

Gets replaced with its expansion

prior to compilationThursday, 19 September 13

Runtime

(arg-logger (+ 2 3))

;;"Called with: " ((+ 2 3))

Arguments are handed into macros unevaluated

Thursday, 19 September 13

More on that later

Thursday, 19 September 13

Debugging macros

(macroexpand '(cond true "true" :else "false"))

;;(if true "true" (clojure.core/cond :else "false"))

Thursday, 19 September 13

Debugging macros(require '[clojure.walk :as w])

(w/macroexpand-all '(cond true "true" :else "false"))

;;(if true "true" (if :else "false" nil))

Thursday, 19 September 13

Quoting Prevents evaluation

(def my-list (1 2 3)) ;java.lang.Integer cannot be cast to clojure.lang.IFn

(def my-list '(1 2 3)) ;Success!

'my-list ;my-list'(1 2 3) ;(1 2 3)

Thursday, 19 September 13

Syntax-quote

Automatically qualifies all unqualified symbols

`my-list ; user/my-list`prn ; clojure.core/prn

Note the backtick!

Thursday, 19 September 13

Unquote

Evaluates some forms in a quoted expression

Before unquoting...`(map even? my-list);;(clojure.core/map clojure.core/even? user/my-list)

After...`(map even? '~my-list);;(clojure.core/map clojure.core/even? (quote (1 2 3)))

Thursday, 19 September 13

Unquote-splicing

“Unpacks” a sequence

Before unquote-splicing...

`(+ ~my-list) ;;(clojure.core/+ (1 2 3))

(eval `(+ ~my-list)) ;;java.lang.Integer cannot be cast to clojure.lang.IFn

Thursday, 19 September 13

Unquote-splicing

After...

`(+ ~@my-list) ;;(clojure.core/+ 1 2 3)

(eval `(+ ~@my-list)) ;;6

Thursday, 19 September 13

All of these are useful inside macros!

Thursday, 19 September 13

Jam time!

Thursday, 19 September 13

Jam time!

•Get the code - http://bit.ly/cufp-2013-macros•Make sure you have leiningen 2.x installed•Run $lein midje or lein midje :autotest from the project root•Watch the tests fail!•Fix them :)

Thursday, 19 September 13

Summary(defmacro macro-name [& args] ...)Defining macros

(def my-list '(1 2 3))Quoting

`(+ my-list) ; (clojure.core/+ user/my-list)Syntax-quote

`(+ ~my-list) ; (clojure.core/+ (1 2 3))Unquote

(macroexpand '...)

(require '[clojure.walk :as w])(w/macroexpand-all '...)

Debugging

Unquote-splicing `(+ ~@my-list) ; (clojure.core/+ 1 2 3)

Thursday, 19 September 13

Questions?Leonardo Borges

@leonardo_borgeswww.leonardoborges.comwww.thoughtworks.com

Thursday, 19 September 13

Go deeper

Let Over Lambda - http://bit.ly/let-over-lambda

On Lisp - http://bit.ly/on-lisp-book

Thursday, 19 September 13