2011-03-10 14 views
8

Tengo dos macros. El primero toma un símbolo como el único parámetro (porque se pasa a def, que necesita un símbolo). La segunda función toma una lista de símbolos y debe llamar a la primera con cada símbolo individualmente.En clojure, ¿cómo puedo evaluar los argumentos a una macro desde otra macro?

(defmacro m1 [s] 
    '(let [f# ... dynamic function definition ...] 
     (def ~s f#)) 

La segunda macro debe tener una lista de símbolos y pasarlos a la primera, pero no puedo hacer que funcione. Lo mejor que podía venir fue el siguiente:

(defmacro m2 [symbols] 
    `(for [s# ~symbols] (eval (read-string (str "(name.of.current.namespace/m1 " s# ")"))))) 

que obliga al s# a ser evaluados antes de pasar a la primera macro. También se invoca con una lista de cadenas, en lugar de una lista de símbolos.

Esto es útil para una biblioteca que estoy usando para la cual todas las funciones en la biblioteca toman los mismos dos primeros parámetros. Estoy intentando crear funciones de envoltura, en mi espacio de nombres, para algunas de las funciones, que proporciona automáticamente los primeros dos valores de parámetros que son comunes a todos ellos.

¿Alguna idea para mejorar esto?

+1

¿Por qué un fn llano no es una opción aquí? Algo parecido a '(defn m2fun [symbols] (map m1 symbols))' – skuro

+0

¿Puedes usar partial? Realmente no entiendo cuál es el problema. – nickik

Respuesta

8

Normalmente, cuando se pregunta cómo conseguir que cooperen dos macros, la respuesta es no convertirlas en ambas macros. Creo que mi entrada de blog en macro-writing macros me ayudará a aclarar. Por esta situación particular probablemente me combino dos sugerencias de los comentarios:

(defmacro build-simpler-functions [& names] 
    (cons 'do 
     (for [name names] 
      `(def ~(symbol (str "simple-" name)) 
      (partial ~name 5 10))))) ; if you always pass 5 and 10 

(build-simpler-functions f1 f2) 

Esto expande a

(do 
    (def simple-f1 (clojure.core/partial f1 5 10)) 
    (def simple-f2 (clojure.core/partial f2 5 10))) 

que se parece básicamente lo que quiere.

Editar: si los argumentos que pasas "siempre" son diferentes para cada función, usted puede hacer esto:

(defmacro build-simpler-functions [& names] 
    (cons 'do 
     (for [[name & args] names] 
      `(def ~(symbol (str "simple-" name)) 
      (partial ~name [email protected]))))) 

(build-simpler-functions [f1 5 10] [f2 "Yo dawg"]) ; expansion similar to the previous 
+0

Este es spot-on: The (cons 'do ...) y ~ (symbol s # ...) hicieron la cosa. El tornillo de la mente se estaba desviando de la idea de qué código se está ejecutando y qué código se está devolviendo y cómo se influye entre sí. –

+0

También te puede interesar clojure.contrib.macro-utils.macrolet para macros puntuales como este. No me gusta contaminar mis espacios de nombres con macros. Solo uso una vez, así que podrías escribir (macrolet [(defpartial [y funcs] (cons 'do ...))]] (defpartial [f1 5 10] [f2' omg ' más 'args])) – amalloy

Cuestiones relacionadas