Unlike Common Lisp, Clojure doesn't support user-defined reader macros. You can read some of the rationale for why in this chat log, among other places. I think that's probably a good decision; I don't see a lot of need for mangling the reader. Regular macros get you pretty far already and Clojure has built-in reader support for all the good stuff.
But how hard would it be to have custom reader macros in Clojure if you wanted them? Turns out not too hard if you're willing to ruthlessly break encapsulation and rely on implementation details. Here's one way you could define a dispatch reader macro (i.e. one starting with
# and some specified second character):
(defn dispatch-reader-macro [ch fun] (let [dm (.get (doto (.getDeclaredField clojure.lang.LispReader "dispatchMacros") (.setAccessible true)) nil)] (aset dm (int ch) fun)))
Pass in a character and an fn and you get a reader macro. For a silly example let's make reader syntax to uppercase a literal string.
(defn uppercase-string [rdr letter-u] (let [c (.read rdr)] (if (= c (int \")) (.toUpperCase (.invoke (clojure.lang.LispReader$StringReader.) rdr c)) (throw (Exception. (str "Reader barfed on " (char c)))))))
The function is passed a reader and the dispatch character (which you can usually ignore). I cheat and use Clojure's
StringReader to do the real work.
Now I can do this:
user> (dispatch-reader-macro \U uppercase-string) #<user$uppercase_string__1295 user$uppercase_string__1295@9b59a2> user> #U"Foo bar BAZ" "FOO BAR BAZ" user> (println #U"foo\nbar") FOO BAR nil user> #U(blarg) java.lang.Exception: Reader barfed on ( (= "FOO" "foo") false (= "FOO" #U"foo") true
Oh sweet Jesus don't use this in real code, because:
- The community will rightly hunt you down with torches and pitchforks.
- Reader macro characters are reserved and may conflict with later changes to the core language.
- These are set globally, not per-namespace.
- And so on. Just don't.
But I think it's a nice demonstration. I've read opinions that Clojure isn't a Real Lisp™ because a lot of Clojure is written in Java and isn't extensible in Clojure itself, but that's generally not true. The reader code for Clojure was all written in Java, but above I modify it from Clojure. There is no line separating Java-land and Clojure-land. It's all one big happy family.