#? reader conditional

syntaxsince v0.0-3190 in clojureEdit

Use a different expression depending on the compiler (e.g. Clojure vs ClojureScript).

  • #?(:clj ... :cljs ...)

Details:

(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.

For Macros

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.


Examples:

#?(: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

See Also:


Reader code @ tools.reader:src/main/clojure/clojure/tools/reader.clj
(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)))

Reader table @ tools.reader:src/main/clojure/clojure/tools/reader.clj
(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))