Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Writing Macros

303 views

Published on

Clojure studygroup 2016.8.11

Published in: Software
  • Be the first to comment

  • Be the first to like this

Writing Macros

  1. 1. Writing Macros Chapter 8 of CLOJURE for the BRAVE and TRUE ( LINK : http://www.braveclojure.com/writing-macros/ ) 2016/8/11 - ClojureTW 讀書會 @ 摩茲工寮, 台北
  2. 2. Macros Are Essential ● Macros are an integral part of Clojure development — they’re even used to provide fundamental operations. ● EXAMPLE: “ when ” = “ if ” + “ do ” ( macroexpand ‘( when boolean-expression expression-1 expression-2 expression-3)) ; => (if boolean-expression ; (do expression-1 ; expression-2 ; expression-3))
  3. 3. Anatomy of a Macro Macros receive unevaluated, arbitrary data structures as arguments and return data structures that Clojure evaluates defmacro defn (evaluated)
  4. 4. Building Lists for Evaluation ● Macro writing is all about building a list for Clojure to evaluate defmacro final list to evaluate unevaluated arguments
  5. 5. Building Lists for Evaluation ● Macro writing is all about building a list for Clojure to evaluate (defmacro infix-2 [[operand1 op operand2]] (list op operand1 operand2))
  6. 6. Building Lists for Evaluation ● You’ll need to be extra careful about the difference between a symbol and its value Macro (building lists) closed-box (list of symbol) Evaluation
  7. 7. Building Lists for Evaluation ● You’ll need to be extra careful about the difference between a symbol and its value (defmacro my-print-whoopsie [expression] (list let [result expression] (list println result) result)) EXCEPTION
  8. 8. Building Lists for Evaluation ● You’ll need to be extra careful about the difference between a symbol and its value (defmacro my-print-whoopsie [expression] (list 'let ['result expression] (list 'println 'result) result)) OK!
  9. 9. Building Lists for Evaluation Single Quoting vs. Syntax Quoting '+ ; => + `+ ; => clojure.core/+ Syntax quoting will always include the symbol’s full namespace → Help you avoid name collisions
  10. 10. Building Lists for Evaluation Single Quoting vs. Syntax Quoting '(+ 1 ~(inc 1)) ; => (+ 1 ~(inc 1)) `(+ 1 ~(inc 1)) ; => (clojure.core/+ 1 2) full namespace evaluated instead of being quoted!
  11. 11. Building Lists for Evaluation Single Quoting vs. Syntax Quoting The other difference between quoting and syntax quoting is that the latter allows you to unquote forms using the tilde, ~ ( q u o t e d ) ‘ ( q u o t e d ` ~ ( evaluated ) q u o t e d )
  12. 12. Using Syntax Quoting in a Macro If you want your macro to return multiple forms for Clojure to evaluate, make sure to wrap them in a do. (defmacro code-critic "Phrases are courtesy Hermes Conrad from Futurama" [bad good] `(do (println "Great squid of Madrid, this is bad code:" (quote ~bad)) (println "Sweet gorilla of Manila, this is good code:" (quote ~good))))
  13. 13. Refactoring a Macro and Unquote Splicing (do ((clojure.core/println "criticism" '(1 + 1)) (clojure.core/println "criticism" '(+ 1 1)))) (do (nil nil)) EXCEPTION NullPointerException nil function nil argument Unquote splicing was invented precisely to handle this kind of situation
  14. 14. Refactoring a Macro and Unquote Splicing `(+ ~(list 1 2 3)) ; => (clojure.core/+ (1 2 3)) `(+ ~@(list 1 2 3)) ; => (clojure.core/+ 1 2 3) Great ! (defmacro code-critic [{:keys [good bad]}] `(do ~@(map #(apply criticize-code %) [["Sweet lion of Zion, this is bad code:" bad] ["Great cow of Moscow, this is good code:" good]]))) do & map so convenient !
  15. 15. Refactoring a Macro and Unquote Splicing ( q u o t e d ` ~ ( evaluated ) q u o t e d ) ( q u o t e d ) ‘ ( q u o t e d ` ~ ( evaluated ) q u o t e d )@ Unquote simple function simple function + do, map … etc.
  16. 16. Things to Watch Out For 1. Variable Capture (def message "Good job!") (defmacro with-mischief [& stuff-to-do] (concat (list 'let ['message "Oh, big deal!"]) stuff-to-do)) (with-mischief (println "Here's how I feel about that thing you did: " message)) ; => Here's how I feel about that thing you did:Oh, big deal!
  17. 17. Things to Watch Out For 1. Variable Capture Uncorrelated Macro letB -> C function ( input: A) ( output: A) function ( input: A) ( B ->C) ( output: A) function ( input: B) ( B ->C) ( output: C) B B B C
  18. 18. Things to Watch Out For 1. Variable Capture (def message "Good job!") (defmacro with-mischief [& stuff-to-do] `(let [message "Oh, big deal!" ] ~@stuff-to-do)) (with-mischief (println "Here's how I feel about that thing you did: " message)) ; Exception: Can't let qualified name: user/message Syntax quoting is designed to prevent you from accidentally capturing variables within macros.
  19. 19. Things to Watch Out For 1. Variable Capture (defmacro without-mischief [& stuff-to-do] (let [macro-message (gensym 'message)] `(let [~macro-message "Oh, big deal!" ] ~@stuff-to-do (println "I still need to say: " ~macro-message)))) (without-mischief (println "Here's how I feel about that thing you did: " message)) ; => Here's how I feel about that thing you did: Good job! (gensym 'message) ; => message4763 Symbol prefix ! This example avoids variable capture by using gensym to create a new, unique symbol that then gets bound to macro-variable.
  20. 20. Things to Watch Out For 1. Variable Capture Uncorrelated Macro gensym let B -> C function ( input: A) ( output: A) function ( input: A) ( gensym B ->C) ( output: A) function ( input: B) ( B123 ->C) ( output: B) B B B B
  21. 21. Things to Watch Out For 1. Variable Capture `(blarg# blarg#) (blarg__2869__auto__ blarg__2869__auto__) `(let [name# "Larry Potter" ] name#) ; => (clojure.core/let [name__2872__auto__ "Larry Potter"] name__2872__auto__) Because this is such a common pattern, you can use an auto-gensym. Auto-gensyms are more concise and convenient ways to use gensyms
  22. 22. Things to Watch Out For ( q u o t e d ` ~ ( evaluated ) q u o t e d ) ( q u o t e d ` ~ ( evaluated ) q u o t e d )@ Unquote + do, map … etc. ( quoted ` ~ ( evaluated )( let [ var # … ] autogem
  23. 23. Things to Watch Out For 2. Double Evaluation (defmacro report [to-try] `(if ~to-try (println (quote ~to-try) "was successful:" ~to-try) (println (quote ~to-try) "was not successful:" ~to-try))) 1 2 3
  24. 24. Things to Watch Out For 2. Double Evaluation Repeated actions in Macro let repeat 100 times function ( input: A) a lot of calc ( output: A) function ( input: A) a lot of repeating calc ( output: A) B B B function ( input: A) a lot of repeating calc ( output: A) Waiting for calculation process ….
  25. 25. Things to Watch Out For 2. Double Evaluation (defmacro report [to-try] `(if ~to-try (println (quote ~to-try) "was successful:" ~to-try) (println (quote ~to-try) "was not successful:" ~to-try))) 1 2 3 (defmacro report [to-try] `(let [result# ~to-try] (if result# (println (quote ~to-try) "was successful:" result#) (println (quote ~to-try) "was not successful:" result#)))) 1 Double eval ? → let → Var capture ? → auto-gensym
  26. 26. Things to Watch Out For 3. Macros All the Way Down (doseq [code ['(= 1 1) '(= 1 2)]] (report code)) ; => code was successful: (= 1 1) ; => code was successful: (= 1 2) (defmacro doseq-macro [macroname & args] `(do ~@(map (fn [arg] (list macroname arg)) args))) (doseq-macro report (= 1 1) (= 1 2)) ; => (= 1 1) was successful: true ; => (= 1 2) was not successful: false report operating at macro expansion time, just can’t access those values. To resolve this situation, you might write another macro
  27. 27. Things to Watch Out For 3. Macros All the Way Down ● It’s easy to paint yourself into a corner, making it impossible to accomplish anything with run-of-the-mill function calls. ● They only really compose with each other, so by using them, you might be missing out on the other kinds of composition (functional, object-oriented) available to you in Clojure. Macro Macro Macro Macro Macro Macro Macro Macro Macro Macro Macro Macro
  28. 28. Summary Don’t listen to these prudes — at least, not at first! Go out there and have a good time. That’s the only way you’ll learn the situations where it’s appropriate to use macros. You’ll come out the other side knowing how to use macros with skill and panache.

×