defmacro

macrosince v0.0-927 clojure.core/defmacroEdit
(defmacro name doc-string? attr-map? [params*] body)
(defmacro name doc-string? attr-map? ([params*] body) + attr-map?)

Details:

Defines a macro, which is essentially a function that runs at compile time. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages.

Using macros is as easy as using functions, but writing them is a little more difficult. Also, creating macros is generally discouraged if you can accomplish the same goal with a function.

Rules and Details

There is a strict rule for when you can use defmacro -- you can only use it in what we call a macro namespace, effectively forcing you to separate your compile time and runtime code.

A side effect of this is that you cannot use defmacro from a REPL. Sorry!

This strict rule is due to the nature of differing compile time environments for the optimized "ClojureScript JVM" compiler and the newer bootstrapped "ClojureScript JS" compiler.

In order to create macros that are portable between either compiler version, you must place macros in a .cljc file, but a .clj file is sufficient if no reader conditionals are needed. Why would they be needed? Because ClojureScript macro namespaces may be handed off to Clojure for evaluation, depending on the compiler version:

compiler version macro namespaces evaluated by
ClojureScript JVM Clojure
ClojureScript JS ClojureScript

Please see the examples section below for a more concrete look.


Examples:

Here is a str->int macro that works for either ClojureScript compiler version. It simply expands to a js/parseInt call:

;; in macros.clj
(ns foo.macros)

;; expands to a runtime call
(defmacro str->int [s]
  `(js/parseInt s))

If we want to evaluate the conversion at compile time instead of expanding it to a runtime call, we must use reader conditionals (in a .cljc file) to choose the function appropriate for each compiler's evaluation environment.

;; in macros.cljc
(ns foo.macros)

;; expands to the result of the conversion
(defmacro str->int [s]
  #?(:clj  (Integer/parseInt s)
     :cljs (js/parseInt s)))

See Also:


Source docstring:
Like defn, but the resulting function name is declared as a
macro and will be used as a macro by the compiler when it is
called.
Source code @ clojurescript:src/main/clojure/cljs/core.cljc
(core/defn defmacro
  {:arglists '([name doc-string? attr-map? [params*] body]
               [name doc-string? attr-map? ([params*] body)+ attr-map?])
   :macro true}
  [&form &env name & args]
  (core/let [prefix (core/loop [p (core/list (vary-meta name assoc :macro true)) args args]
                      (core/let [f (first args)]
                        (if (core/string? f)
                          (recur (cons f p) (next args))
                          (if (map? f)
                            (recur (cons f p) (next args))
                            p))))
             fdecl (core/loop [fd args]
                     (if (core/string? (first fd))
                       (recur (next fd))
                       (if (map? (first fd))
                         (recur (next fd))
                         fd)))
             fdecl (if (vector? (first fdecl))
                     (core/list fdecl)
                     fdecl)
             add-implicit-args (core/fn [fd]
                                 (core/let [args (first fd)]
                                   (cons (vec (cons '&form (cons '&env args))) (next fd))))
             add-args (core/fn [acc ds]
                        (if (core/nil? ds)
                          acc
                          (core/let [d (first ds)]
                            (if (map? d)
                              (conj acc d)
                              (recur (conj acc (add-implicit-args d)) (next ds))))))
             fdecl (seq (add-args [] fdecl))
             decl (core/loop [p prefix d fdecl]
                    (if p
                      (recur (next p) (cons (first p) d))
                      d))]
    `(let [ret# ~(cons `defn decl)]
       (set! (. ~name ~'-cljs$lang$macro) true)
       ret#)))