2012-09-25 28 views
5

Esto es similar al problema discutido en Treat Clojure macro as a function pero al intentar el enfoque en la respuesta superior, recibí un error. Con suerte demasiada información respecto a mi solicitud específica no es necesario, ya que es bastante complicado, pero aquí es una versión destilada de lo que he intentado hacer:Clojure macro as function/'Partial' for Macros?

(defmacro make-fn [m arg1] 
    `(fn [& args#] 
     (eval `(~'~m ~'~arg1 [email protected]#)))) 

he utilizado la macro en este contexto:

(let [columns (make-columns table-width) 
     table-name (keyword (str "table_" n))] 
    (apply (make-fn helpers/tbl table-name) columns)) 

"helpers/tbl" es una macro que espera una palabra clave de nombre de tabla y una cantidad variable de listas que contienen especificaciones de columna (como [: varchar 100] o algo así). Estoy intentando crear especificaciones de tablas de bases de datos aleatorias sobre la marcha para facilitar algunas pruebas. De todos modos, al intentar ejecutar el código anterior, me sale el siguiente error:

CompilerException java.lang.RuntimeException: Unable to resolve symbol: table-name in this context, compiling:(NO_SOURCE_PATH:1) 

en cierto modo me agarro el problema: la expansión de macros se realiza en tiempo de compilación, y yo estoy tratando de incluir un valor de tiempo de ejecución en el expansión macro, de ahí el extraño uso de las comillas y los comicios para configurar todo correctamente. Básicamente quiero un par de macros, y necesito poder reutilizar este mecanismo para diferentes macros en diferentes espacios de nombres, y tener toda la resolución de la variable correcta. ¿Esto es posible?

Respuesta

2

El problema está causado por la forma en que Clojure resuelve símbolos dentro de una expresión de sintaxis-comilla (retroceso). Para evitar la captura involuntaria de variables, Clojure siempre interpreta los símbolos dentro de una expresión de sintaxis-cita como refiriéndose a Vars (no locales).

Puede solucionar esto "haciendo rodar su propio código de creación de formulario", equivalente al generado por la sintaxis-cita. Es tan feo como el pecado, pero funciona ... simplemente no digas que no te lo advertí:

(defmacro make-fn [m arg1] 
    (let [g (gensym)] 
    (list 'fn ['& g] 
     (list 'eval (list 'concat (list 'list m arg1) g))))) 

Wow, esto es como un flashback a mis días de Common Lisp ...

+0

BTW , hecho interesante sobre 'syntax-quote': se implementa completamente en el lector. Cuando el lector ve un backtick, lee el siguiente formulario, luego lo transforma recursivamente en un código que usa 'seq',' concat' y 'list'. El compilador solo ve el código 'concat' /' list' resultante. –