2011-04-28 7 views
6

Tengo una macro que implementará una interfaz Java que es un oyente. Definí la macro para tomar un mapa que contiene funciones que quiero desestructurar, y uso para cada uno de los métodos de interfaz. Esta es la macro: -Pasando el mapa de funciones a una 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))))) 

El mapa corporal es la siguiente: -

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

Pero cuando llamo (macroexpand '(with-cache-listener-m test-cache e m-callbacks)) se expande a (. test-cache user/addMapListener (clojure.core/proxy [com.tangosol.util.AbstractMapListener] [] (user/entryInserted [e] nil e) (user/entryUpdated [e] nil e)))

Las funciones de devolución de llamada son nulas. ¿Tengo que definirlos de manera diferente o estoy haciendo esto de la manera incorrecta?

+0

¿Qué son la inserción de devolución de llamada y la actualización de devolución de llamada? Cuando llamas m-callbacks, ¿qué produce? – rplevy

+0

Son solo dos funciones que imprimen su argumento y regresan. m-callbacks es usuario> m-callbacks {: f-insert # ,: f-update # } – JPT

+0

¿Qué pasa si enlaza m-devoluciones de llamada a una función que evalúa este mapa en lugar de al mapa directamente? – rplevy

Respuesta

4

Cuando llama a la macro with-cache-listener-m, el argumento body se limita a 'm-callbacks como un símbolo, por lo que cuando intente desestructurar esa var local no funcionará porque no es un mapa. Usted puede dejar que la forma resultante de hacer el trabajo de esta manera:

(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))))) 

Pero al final no estoy seguro de su código necesita una macro, has de escribir como una función:

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

como se ha visto, he cambiado un par de cosas:

  • Hecho el nombre de función más clara, la macro no era realmente como otras macros con- * que por lo general evalúan algún código (el body) en una especie de especial contexto.
  • Se eliminó el argumento del evento, ya que no parecía tener ningún uso.
  • Hizo explícitos los argumentos insert-fn y update-fn para simplificar el ejemplo.
  • Se utilizó la nueva sintaxis de llamada al método.
  • Se corrigieron los métodos del proxy para usar realmente las funciones dadas.

Si desea realizar las funciones completamente opcional y hacer posible que ha de darse en cualquier orden siempre se puede hacer eso:

(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))))))) 

Tenga en cuenta que he añadido código para no llamar cuando addMapListener no se dan funciones

+0

Esto es mucho más claro , gracias. Sin embargo, sigo teniendo el mismo error, como cuando originalmente lo definí como una función. Mi mapa de funciones es (def m-callbacks {: insert-fn callback-insert: update-fn callback-update}), pero cuando llamo add-map-listener, obtengo una excepción, No se proporciona ningún valor para la clave. m-callbacks evals para usuario> m-callbacks {: insert-fn # ,: update-fn # } – JPT

+0

Primero, usted Necesito usar 'apply' como este' (aplicar add-map-listener m-callbacks) 'y segundo he cometido un pequeño error, el argumento' functions' necesita transformarse de nuevo en un mapa, ahora está arreglado. –

+0

También podría definir 'add-map-listener' con ese vector args:' [component functions] 'para que tome directamente un mapa como argumento. Con la forma en que lo he definido, podría llamarlo así: '(add-map-listener test-cache: f-insert callback-insert: f-update callback-update)' –

1

Las macros no son funciones: solo conocen los formularios literales que se les pasan en tiempo de compilación. Si asigna un valor, digamos 10, al var x, pase x a su macro, no ve 10 sino x. Su macro probablemente funcionará bien si, en lugar de def ing m-callbacks y luego pasa ese símbolo, simplemente pase el mapa directamente como un literal.

+0

Creo que lo que realmente quiero saber es cómo cualquier interfaz de escucha de Java que define más de un método, se puede implementar idiomáticamente en una macro. Intenté esto: - usuario> (macroexpand '(con-caché-oyente-m prueba-caché e' {: f-inserción devolución de llamada-inserción: f-actualización devolución de llamada})) (. cache user/addMapListener (clojure.core/proxy [com.tangosol.util.AbstractMapListener] [] (user/entryInserted [e] nil e) (user/entryUpdated [e] nil e))) – JPT

Cuestiones relacionadas