Clojure macro: perform action only if a symbol is defined

Viewed 417

I have a macro defprinter where I can define functions that: get a value in a dic based on destructuring + print that variable.

This looks like:

(defmacro defprinter [name pattern] 
  (list 'defn name [pattern] '(prn to-print)))

So I can do something like (defprinter print-lemons {to-print :lemons}) and then (print-lemons {:lemons "lemons"}) and it will print the correct thing (and I can define printers with any kind of destructuring on the first argument).

But now I want to give the option of that function maybe knowing how to print with a color as well, such as, if the symbol color is defined, it should do (prn color "-colored " to-print), but else just (prn color).

So, the function generated by (defprinter print-lemons {to-print :lemons}) should work the same as above, while (defprinter print-lemons {to-print :lemons, color :color}) would write a function that performs the colored version.

Moreover, I want the same effect if I do (let [color "black"] (defprinter print-lemons {to-print :lemons})) or (def color "black") (defprinter print-lemons {to-print :lemons}).

Is that possible?

I've tried writing it the following way:

(defmacro defprinter [name pattern] 
  (list 'defn name [pattern]
   '(try (eval '(prn (str color "-colored " to-print))) 
         (catch java.lang.RuntimeException _ 
           (prn to-print)))))

In my understanding the function the macro will write will try to eval the expression at runtime, fail with a RuntimeException if color is not defined and then execute the (prn to-print). And even though it would check for the existence of color at runtime, to-print (which always need to exist for that function) would be checked at compile time when the macro expands.

But what happens here is that I always get a RuntimeException, even when color is defined (even if I leave only to-print on the eval statement, it can't find it but the clause in the catch works fine). Seems like the symbol isn't being resolved as I would expect during eval, but I can't think of any other way to achieve this.

Any suggestions?

2 Answers
Related