A set of portable macro utility functions.
All public functions reside in the macrowbar.core namespace. In the scope of this document, consider this namespace implicitly loaded and aliased to macrowbar.
Some examples below define and then immediately call macros; note that in self-hosted ClojureScript, this is only possible if the macro is defined in a different compilation stage (ie. namespace). Futhermore, if a macro were to resolve a symbol at compile time (via eval or partial-eval), then that symbol has to be in a different compilation stage than the one in which the macro was invoked. To illustrate:
;; src/foo.cljc
(ns foo
#?(:cljs [:require-macros foo]))
(defmacro macro [x]
(macrowbar/eval x))
;; src/bar.cljc
(ns bar
(:require [foo]))
(def y 10)
;; Will not compile in self-hosted ClojureScript!
(foo/macro y)Macro. In Clojure and self-hosted ClojureScript, it always emits the body. In JVM ClojureScript, it if and only if emits the closure constant macrowbar.util/DEBUG is set to true and the mode argument is equal to :debug.
Can be used to strip away all unnecessary compile-time code from JVM ClojureScript js output files.
Example:
;; Emitted in all targets
(def n 1)
;; Emitted in Clojure and self-hosted ClojureScript, but not in JVM ClojureScript
(macrowbar/emit :debug-self-hosted ; could actually be any keyword other than `:debug`
(def n 1))
;; Emitted in Clojure and self-hosted ClojureScript, also in JVM ClojureScript if and only if DEBUG is set
(macrowbar/emit :debug
(def n 1))This function expects the hidden &env argument of a macro as the single argument, and returns true if that macro is being compiled as a ClojureScript macro (i.e. self-hosted).
Example:
(defmacro macro []
(if (macrowbar/cljs? &env)
:cljs
:clj))
(println (macro))
;; => prints :clj in Clojure and JVM ClojureScript, :cljs in self-hosted ClojureScriptPrewalks the given expression, and evaluates each subvalue marked with an 'eval tag.
Example:
(defmacro macro [x]
`(+ ~@(repeat (macrowbar/partial-eval x) 1)))
(macroexpand '(macro ^eval (+ 1 2)))
;; => (clojure.core/+ 1 1 1)
(macro ^eval (+ 1 2))
;; => 3Utility macro for macros, expected to be used at compile time. Takes a map with optional keys :gen, :bind and :eval - each mapped to a vector of simple symbols - and any number of expressions. For each symbol x mapped to:
:gen, it generates a new symbol with the metadata ofx, and binds it tox:bind: it essentially evaluatesxat the target runtime (hard to describe, see example):eval, it evaluates parts ofx(those marked with^eval) at the target compile time.
Having any duplicate symbols leads to undefined behaviour (GIGO).
Example for :gen:
;; ok, but verbose
(defmacro macro []
(let [a (gensym)
b (vary-meta (gensym) assoc :tag 'long)]
...))
;; better
(defmacro macro []
(macrowbar/with-syms {:gen [a ^long b]}
...))Example for :bind:
;; bad, potential undesired multiple evaluation
(defmacro macro-1 [x]
`(+ ~x ~x))
;; better, but a new binding has to be introduced, can't reuse `x`, verbose
(defmacro macro-2 [x]
(let [x' (gensym)]
`(let [~x' ~x]
(+ ~x' ~x'))))
;; better, can reuse `x`, but still verbose
(defmacro macro-3 [x]
(let [x' (gensym)]
`(let [~x' ~x]
~(let [x x']
`(+ ~x ~x)))))
;; better
(defmacro macro-4 [x]
(macrowbar/with-syms {:bind [x]}
`(+ ~x ~x)))Example for :eval:
;; ok, but verbose
(defmacro macro [x]
(let [x (macrowbar.core/partial-eval x)]
...))
;; better
(defmacro macro [x]
(macrowbar/with-syms {:eval [x]}
...))Copyright © 2017 Viktor Magyari
Distributed under the Eclipse Public License v1.0.