Skip to content

Instantly share code, notes, and snippets.

@gtrak
Created October 17, 2013 15:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gtrak/7026863 to your computer and use it in GitHub Desktop.
Save gtrak/7026863 to your computer and use it in GitHub Desktop.
(ns playingwithmacros.core
(:require [clojure.java.browse :refer [browse-url]]
[clojure.pprint :as pp]
[clojure.core.strint :as strint]))
(defmacro my-simple-macro
"A docstring"
[first-arg second-arg & varargs]
"A return value of some kind")
(my-simple-macro a 2)
;; When does it actually run?
(defn test1 []
(my-simple-macro 1 2))
;; Add a println side-effect to the macro
;; Re-eval the defn
defmacro
;;; Explanation by example
;;; Debug macro, from http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html
(defmacro dbg
[x]
`(let [x# ~x]
(println '~x "=" x#)
x#))
;; Syntax-quote
`(1 2 3)
'(a b c)
`(a b c)
;; Namespace-qualified symbols
`(+ a b)
;; Unquote
(let [b 1]
`(a ~b))
(read-string "`(a ~b)")
;; Splice
(let [b ["THIS" "IS" "A" "VECTOR"]]
`(a ~b))
(let [b ["THIS" "IS" "A" "VECTOR"]]
`(a ~@b))
;; Gensyms
(gensym)
(defmacro why-gensyms?
[]
`(let [x 1]
x))
(why-gensyms?)
`(let [x 1]
x)
(defmacro why-gensyms?
[]
`(let [x# 1]
x#))
(why-gensyms?)
`(let [x# 1]
x#)
;;; Back to the source
;; What are &form and &env?
(defmacro env-and-form
[& body]
(dbg &env)
(dbg &form)
nil)
;; What's &form?
(env-and-form 23 4234 23)
;; What's &env?
(let [a 1
b 2]
(env-and-form))
;; Metadata on forms
(defmacro meta-env-and-form
[& body]
(dbg (meta &env))
(dbg (meta &form)))
(let [a 1
b 2]
(meta-env-and-form))
(defn pprint-str
[x]
(with-out-str (pp/pprint x)))
(defmacro super-dbg
[x]
`(let [x# ~x]
(printf "dbg %s:%s> %s is %s\n"
~*ns*
~(:line (meta &form))
~(pr-str x)
(pprint-str x#))
(flush)
x#))
(let [a 5]
(super-dbg a))
;;; Tools
(macroexpand '(fn [a]))
(macroexpand-1 '(fn [a]))
;;; Q: In what order are macros expanded?
(defmacro super-fn
[& body]
`(fn ~@body))
(macroexpand '(super-fn [a]))
(macroexpand-1 '(super-fn [a]))
;;; A: outer-first
(clojure.walk/macroexpand-all
'(let [a (fn [] blah)]))
;;; Q: When does the compiler perform macro-expansion?
;; macroexpand-1 gives a hint
;; https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L6605
;;; More useful macros!
;; https://github.com/flatland/useful/blob/develop/src/flatland/useful/map.clj#L5
(let [transforms {:keys keyword
:strs str
:syms identity}]
(defmacro keyed
"Create a map in which, for each symbol S in vars, (keyword S) is a
key mapping to the value of S in the current scope. If passed an optional
:strs or :syms first argument, use strings or symbols as the keys instead."
([vars] `(keyed :keys ~vars))
([key-type vars]
(let [transform (comp (partial list `quote)
(transforms key-type))]
(into {} (map (juxt transform identity) vars))))))
(let [a 1
b 2
c 3]
(keyed [a b c]))
;;; just for fun
(defmacro dbg-macro
[x]
(let [expansion (clojure.walk/macroexpand-all x)]
`(do (printf "dbg-macro> %s expands to %s\n"
~(pr-str x)
'~expansion)
(flush)
~x)))
;; https://github.com/clojure/core.incubator/blob/master/src/main/clojure/clojure/core/strint.clj#L49
(dbg-macro (strint/<< "Chas is great times ~(+ 1 2)!"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment