1
votes

I wrote a macro shown below that works as expected to log parameters passed to functions at run-time.

(use 'robert.hooke)

(defmacro log-func-args [func arg-log-option print-suffix]
  (condp = arg-log-option
    :all   `(add-hook (var ~func) (fn [f# & args#]
                                   (println "****** " (:name (meta (var ~func))) " called with "
                                         ~print-suffix args#)
                                   (apply f# args#) ))

   :first  `(add-hook (var ~func) (fn [f# & args#]
                                   (println "****** " (:name (meta (var ~func))) " called with "
                                         ~print-suffix (first args#))
                                   (apply f# args#) ))

   :rest  `(add-hook (var ~func) (fn [f# & args#]
                                   (println "****** " (:name (meta (var ~func))) " called with "
                                         ~print-suffix (rest args#))
                                   (apply f# args#) ))
   )  )

This is then used like so:

(log-func-args my-func-name :rest "[server,buyer]: ")

The above macro seemed a bit verbose so I tried to refactor it so that instead of the multiple add-hook's, I have just one with the condp rolled inside, something like so:

(defmacro log-func-args [func arg-log-option print-suffix]
 `(add-hook (var ~func) (fn [f# & args#]
                       ~(condp = arg-log-option
                          :all    `(println "****** " (:name (meta (var ~func))) " called with "
                                        ~print-suffix args#)
                          :first  `(println "****** " (:name (meta (var ~func))) " called with "
                                           ~print-suffix (first args#))
                          :rest   `(println "****** " (:name (meta (var ~func))) " called with "
                                         ~print-suffix (rest args#))                              
                          )

                       (apply f# args#)  ))

This gives me a RuntimeException complaining that the args# in the println cannot be resolved (because the generated symbol for args# in the two syntax-quoted forms - the add-hook form and the println form - is not the same). What is a clean remedy for this?

2

2 Answers

1
votes

Have a look at potemkin's unify-gensyms function. It allows you to use two hashes (##) instead of one to denote values that should be replaced with the same generated symbol.

The more gensyms spanning multiple forms you have, the better this solution becomes (compared to the explicit let/gensym one).

0
votes

Found a solution (courtesy Programming Clojure book) using the gensym func with a let. This allows both syntax-quoted forms to share the same args symbol. Hope this helps someone else. Would love to see any other solutions.

(defmacro log-func-args2 [func arg-log-option print-suffix]
  (let [args (gensym "args")]
    `(add-hook (var ~func) (fn [f# & ~args]
                              ~(condp = arg-log-option
                                 :all    `(println "****** " (:name (meta (var ~func))) " called with "
                                              ~print-suffix ~args)
                                 :first  `(println "****** " (:name (meta (var ~func))) " called with "
                                              ~print-suffix (first ~args))
                                 :rest   `(println "****** " (:name (meta (var ~func))) " called with "
                                              ~print-suffix (rest ~args)) )

                              (apply f# ~args)  )) )  )