syntax | since v0.0-3190 | in clojure | Edit |
Use a different expression depending on the compiler (e.g. Clojure vs ClojureScript).
#?(:clj ... :cljs ...)
(Only allowed in .cljc
files or the REPL)
Allows the reader to conditionally select from the given list of forms depending on available "feature" keys. For example:
#?(:clj "Clojure"
:cljs "ClojureScript")
ClojureScript's reader is configured with the :cljs
feature key, making the
expression above read as "ClojureScript"
. Clojure's reader is
similarly configured with the :clj
key.
This essentially allows us to write portable code for use in both Clojure and ClojureScript.
Reader conditionals are especially important when writing macros in ClojureScript, since the macros may be handed off to Clojure for evaluation, depending on the ClojureScript compiler version:
compiler version | macros evaluated by |
---|---|
ClojureScript JVM | Clojure |
ClojureScript JS | ClojureScript |
Thus, reader conditionals allow us to account for differences in both versions
of the compiler. See defmacro
for details.
#?(:clj "Clojure" :cljs "ClojureScript")
;;=> "ClojureScript"
A function that works in Clojure and ClojureScript (source):
(defn str->int [s]
#?(:clj (java.lang.Integer/parseInt s)
:cljs (js/parseInt s)))
(str->int "123")
;;=> 123
(defn- read-cond
[rdr _ opts pending-forms]
(when (not (and opts (#{:allow :preserve} (:read-cond opts))))
(throw (RuntimeException. "Conditional read not allowed")))
(if-let [ch (read-char rdr)]
(let [splicing (= ch \@)
ch (if splicing (read-char rdr) ch)]
(when splicing
(when-not *read-delim*
(err/reader-error rdr "cond-splice not in list")))
(if-let [ch (if (whitespace? ch) (read-past whitespace? rdr) ch)]
(if (not= ch \()
(throw (RuntimeException. "read-cond body must be a list"))
(binding [*suppress-read* (or *suppress-read* (= :preserve (:read-cond opts)))]
(if *suppress-read*
(reader-conditional (read-list rdr ch opts pending-forms) splicing)
(read-cond-delimited rdr splicing opts pending-forms))))
(err/throw-eof-in-character rdr)))
(err/throw-eof-in-character rdr)))
(defn- dispatch-macros [ch]
(case ch
\^ read-meta ;deprecated
\' (wrapping-reader 'var)
\( read-fn
\= read-eval
\{ read-set
\< (throwing-reader "Unreadable form")
\" read-regex
\! read-comment
\_ read-discard
\? read-cond
\: read-namespaced-map
\# read-symbolic-value
nil))