Defining your own reader macros in Clojure

Back to SIG Clojure Tutorials

You can define your own reader macros in Clojure by creating a "data_readers.clj" file in the top level of your project classpath (e.g., one of the directories included in the `:paths` vector of your project's "deps.edn" file).

This file should contain a map of namespace-qualified symbols (which will be used as reader tags) to the namespace-qualified functions that will process the next output of the Clojure reader.

Here is an example "data_readers.clj" (that you might place in your project's "src" directory):

{sig/inline triangulum.utils/eval-inline-symbols}

You then need to define the relevant functions in your project's code base before this will work. Here's a definition for the example above.

(ns triangulum.utils
  (:require [clojure.string :as s]))

(defn eval-inline-symbols [s]
  (loop [char-seq (seq s)
         acc      []]
    (if (empty? char-seq)
      `(apply str ~acc)
      (if (= \` (first char-seq))
        (recur (->> char-seq (rest) (drop-while #(not= \` %)) (rest))
               (->> char-seq (rest) (take-while #(not= \` %)) (apply str) (s/trim) (symbol) (conj acc)))
        (recur (->> char-seq (drop-while #(not= \` %)))
               (->> char-seq (take-while #(not= \` %)) (apply str) (conj acc)))))))

Once you've set this up, you will be able to define strings in your project that will automatically evalute symbols referenced within them provided that they are surrounded with backticks. Here's an example:

(let [visible-id 100] #sig/inline "Received `visible-id` for this plot.")
;;=> "Received 100 for this plot."

Slick, right?

Have fun and happy hacking with the world's most extensible programming language!

~Gary