Clojure Koans 24 Macros Notebook
Original Clojure Koans: https://github.com/functional-koans/clojure-koans/
Clojure Koans Spoilers Alert
Preface: An experienced Clojure developer friend has told me to not use macros for now, loosely paraphrasing them: "Macros are an advanced technique, best to be revisited with more experience, a clear need, and defined use-case."
{:deps {org.clojure/clojure {:mvn/version "1.10.1"}
;; complient is used for autocompletion
;; add your libs here (and restart the runtime to pick up changes)
compliment/compliment {:mvn/version "0.3.9"}}}
{:hello (clojure-version)}
Initial Thoughts
Macros seem foreign and new to me as a first time LISPer. What new powers will they help me unlock as a programmer?
(defmacro hello [x]
(str "Hello, " x))
(defmacro infix [form]
(list (second form) (first form) (nth form 2)))
;; note: unused-public-var linter warning from clojure-lsp
(defmacro infix-concise [form]
((second form) ; Note the syntax-quote (`) and unquote (~) characters!
(first form)
(nth form 2)))
;; question 24008: What exactly does the "unquote" (~) character do?
;; question 24009: What exactly does the "syntax-qyote" (`) character do?
;; question 24010: How advanced exactly are macros, and what learning prerequisites are appropriate/suitable?
;; question 24011: What are great use cases for macros?
;; question 24012: What are common pitfalls for first time macro writers?
(defmacro recursive-infix [form]
(cond (not (seq? form))
form
(= 1 (count form))
(recursive-infix (first form))
:else
(let [operator (second form)
first-arg (first form)
others (rest (rest form))]
(operator
(recursive-infix first-arg)
(recursive-infix others)))))
;; "Macros are like functions created at compile time"
(= "Hello, Macros!" (hello "Macros!"))
;; ~~question 24001: What is the significance (and consequences) of macros being created at compile time?~~
;; answer 24001a: High level question, too broad, and not immediately helpful at this stage.
;; skill 24001: Invoke a macro
;; "I can haz infix?"
(= 10 (infix (9 + 1)))
;; description: The `infix` macro is used to take in the arguments `9`, `+`, and `1` to execute the addition operation `nine plus one` to get a result of ten. The text above of "I can haz infix?" is a reference to an Internet meme where the grammatically correct English would be be "Can I have infix?"
;; question 24002: Is infix notation useful or helpful to Clojure programmers?
;; answer 24002a: Perhaps when converting code from other languages or regular text... Though I'd still guess no.
;; skill 24002: Invoke a macro that enables non-(Clojure-)standard syntax
;; "Remember, these are nothing but code transformations"
(= (+ 9 1) (macroexpand (infix (9 + 1))))
;; question 24003: What exactly is happening here?
;; question 24004: Why is `infix` quoted?
;; answer 24004a: `infix` is quoted to prevent code execution from
;; happening so that way the `macroexpand` can correctly expand `infix`.
;; question 24005: What is `macroexpand`?
;; answer 24005a: From the docs:
;; "Repeatedly calls macroexpand-1 on form until it no longer represents
;; a macro form, then returns it. Note neither macroexpand-1 nor
;; macroexpand expand macros in subforms."
;; question 24006: What is `macroexpand-1`?
;; answer 24006a: TBD...
;; skill 24003: Expand a macro by using `macroexpand`
;; "You can do better than that - hand crafting FTW!"
(= (* 10 2) (macroexpand (infix-concise (10 * 2))))
;; skill 24004: Write your own custom macro
;; "Things don't always work as you would like them to... "
(= (+ 10 (2 * 3)) (macroexpand (infix-concise (10 + (2 * 3)))))
;; question 24007: Is this Koan exercise in the wrong order?
;; "Really, you don't understand recursion until you understand recursion"
(= 36 (recursive-infix (10 + (2 * 3) + (4 * 5))))
Closing Thoughts
While excited to try using macros, I am thinking, after speaking with a Clojure friend, that I am best off to indeed "shelve them for now." I wonder how many other Clojure Koans sections, as well as the "well advertised benefits of Clojure" (e.g. as listed on clojure.org) are too advanced/complex for a Clojure/LISP initiate to use correctly/effectively.
;; skill 24005: Rewrite a macro as a function
To-Do's
[ ] Add research here to helpful macro use-cases
[ ] Consider (brainstorm, research) how might I build my macro thinking skills before I need to use them