1
votes

I have run into this problem... I have two macros that look very similar

(import java.lang.management.ManagementFactory)

(defmacro with-thread-manager [tm & body]
  {:pre [(and (vector? tm) (= 1 (count tm)))]}
  `(let [~(first tm) (ManagementFactory/getThreadMXBean)]
     ~@body))

(defmacro with-os-manager [tm & body]
  {:pre [(and (vector? tm) (= 1 (count tm)))]}
  `(let [~(first tm) (ManagementFactory/getOperatingSystemMXBean)]
     ~@body))

they are used as follows:

(defn thread-count []
  (with-thread-manager [tm] (.getThreadCount tm)))

(thread-count)
;; => 12

(defn application-cpu-time []
  (with-os-manager [osm] (.getProcessCpuTime osm)))

(application-cpu-time)
;; => 71260000000

I wish to generalise the two with-*-manager into another macro so that I can simpify them like this:

(defmanagementblock with-thread-manager (ManagementFactory/getThreadMXBean))
(defmanagementblock with-os-manager (ManagementFactory/getOperatingSystemMXBean))

so the easiest way I knew was to change the macro a little bit

(defmacro with-thread-manager [tm & body]
  {:pre [(and (vector? tm) (= 1 (count tm)))]}
  (apply list 'let [(first tm) '(ManagementFactory/getThreadMXBean)]
      body))

and to write the block:

(defmacro defmanageblock [name MANAGER]
  (list 'defmacro name '[tm & body]
    '{:pre [(and (vector? tm) (= 1 (count tm)))]}
    (list 'apply 'list ''let (vector '(first tm) 'MANAGER)
          'body)))

everything goes well except the MANAGER does not quote properly. I've tried a bunch of quoting and unquoting option like ' , ` , ~' and many other variations of it. but it does not give the right value.

2

2 Answers

5
votes

One reasonable solution, if you don't mind keeping track of a few scopes in your head:

(defmacro defmanagedblock [name mgr]
  `(defmacro ~name [tm# & body#]
     {:pre [(and (vector? tm#)) (= 1 (count tm#))]}
     `(let [~(first tm#) ~'~mgr]
        ~@body#)))
5
votes

If you define defmanageblock to be a function that takes a symbol describing the factory to use which returns the s-expression as a list then you would have two macros that call functions instead of nested macros . In most cases doing the real code generation work in functions rather than macros makes the code easier to reason about and test