开发者

Passing map of functions to a macro

开发者 https://www.devze.com 2023-03-01 00:24 出处:网络
I have a macro that will implement a Java interface that is a listener. I defined the macro to take a map containing functions that I w开发者_JAVA技巧ant to destructure, and use for each of the interf

I have a macro that will implement a Java interface that is a listener. I defined the macro to take a map containing functions that I w开发者_JAVA技巧ant to destructure, and use for each of the interfaces methods. This is the macro :-

(defmacro with-cache-listener-m [component event body]
   (let [{:keys [f-insert f-update]} body]
     `(. ~component addMapListener
     (proxy [AbstractMapListener] []
       (entryInserted [~event] ~f-insert ~event)
       (entryUpdated [~event] ~f-update ~event)))))

The body map is this :-

(def m-callbacks {:f-insert callback-insert :f-update callback-update})

But when I call (macroexpand '(with-cache-listener-m test-cache e m-callbacks)) it expands to (. test-cache user/addMapListener (clojure.core/proxy [com.tangosol.util.AbstractMapListener] [] (user/entryInserted [e] nil e) (user/entryUpdated [e] nil e)))

The callback functions are nil. Do I need to define them differently or am I going about this the wrong way.


When you call the with-cache-listener-m macro, the body argument get bounded to 'm-callbacks as a symbol, so when you try to destructure that local var it won't work because it ain't a map. You can let the resulting form do the job like this:

(defmacro with-cache-listener-m [component event body]
  `(let [{:keys [f-insert# f-update#]} ~body]
     (. ~component addMapListener
        (proxy [AbstractMapListener] []
          (entryInserted [~event] f-insert# ~event)
          (entryUpdated [~event] f-update# ~event)))))

But in the end I'm not sure your code need a macro, have you tried to write it as a function:

(defn add-map-listener [component insert-fn update-fn]
  (.addMapListener component
    (proxy [AbstractMapListener] []
      (entryInserted [e] (insert-fn e))
      (entryUpdated [e] (update-fn e)))))

As you saw, I changed a couple of things:

  • Made the function name clearer, your macro wasn't really like other with-* macros that usually evaluate some code (the body) in some kind of special context.
  • Removed the event argument as it didn't seemed to have any use.
  • Made the insert-fn and update-fn arguments explicit to simplify the example.
  • Used the new method calling syntax.
  • Fixed the proxy's methods to actually use the given functions.

If you want to make the functions completely optional and make it possible to be given in any order you could always do that:

(defn add-map-listener [component & functions]
  (let [{:keys [insert-fn update-fn]} (into {} functions)]
    (when-not (empty? functions)
      (.addMapListener component 
        (proxy [AbstractMapListener] []
          (entryInserted [e] (insert-fn e))
          (entryUpdated [e] (update-fn e)))))))

Notice that I've added code to not call addMapListener when no functions are given.


Macros are not functions: they only know about the literal forms passed to them at compile-time. If you assign a value, say 10, to the var x, then pass x to your macro, it sees not 10 but x. Your macro will probably work fine if, instead of defing m-callbacks and then passing that symbol, you simply pass the map directly as a literal.

0

精彩评论

暂无评论...
验证码 换一张
取 消