/ Clojure

Refer a macro with CLJC

When you want to refer a macro in CLJC, you get into an interesting situation where Clojure, ClojureScript, and potentially other dialects begin to diverge. For instance, Clojure refers macros in one manner while ClojureScript does so another. How best to handle this split? The answer is reader conditionals if you are using Clojure 1.70 or later.

This post is inspired from chat that occurred in Clojurian Slack's #beginners channel on 2017-03-07 and 2017-03-08. I think the chances are low that the sablono.core/html macro would be used in Clojure (rather than ClojureScript), but it was the original example... thus carried on here.

Reader conditionals allow you to write libraries that can work across Clojure dialects, which improves code portability. A handy guide to reader conditionals exists on Clojure's site and explains the nuances very well. It even covers the situation described here!

For Clojure you could use :refer to access the macro, just like a standard function. However, if you wanted to use a macro in ClojureScript then :refer-macros is what you would use. To better illustrate:

;; Clojure
(ns app.ui
  (:require [rum.core :as rum]
            [sablono.core :refer [html]]))

;; ClojureScript
(ns app.ui
  (:require [rum.core :as rum]
            [sablono.core :refer-macros [html]]))

Should you try to use the above ClojureScript snippet in Clojure then you would run into an issue where the symbol html cannot be resolved. Certainly a problem if you were attempting to create something that worked across dialects. To prevent this, use reader conditionals like so:

;; CLJC
(ns app.ui
  (:require [rum.core :as rum]
            #?(:cljs [sablono.core :refer-macros [html]]
               :clj  [sablono.core :refer [html]])))

And that should be all that is needed to get around the referring macro issue.

Someone later on pointed out that the following could be another alternative since the ClojureScript compiler can usually figure out when it needs to require a macro:

(:require [rum.core :as rum]
          [sablono.core :as s])

;; elsewhere
(s/html [:div "ga"])

This is certainly a viable option as well. So, with that in mind, you should be able to refer (and require) macros as you see fit when using CLJC.