2011-04-03 22 views
15

¿Cómo puedo hacer que una macro Clojure actúe como una función, por lo que puedo pasarla como un argumento, por ejemplo? Esperaría tener que envolverlo de alguna manera.Tratar macro Clojure como una función

No esperaría que la versión empaquetada se comportara exactamente igual que la macro original (diferencias de llamada por nombre vs llamada por valor), pero sería útil en algunos casos donde esto no era importante.

Respuesta

19

Si te estoy entendiendo correctamente, sólo puede envolver en una función.

considerar esta aplicación (tonto) de una función cuadrada como una macro:

(defmacro square [x] 
    (list * x x)) 

Pasando directamente como arg no va a funcionar, como ya saben:

user=> (map square [1 2 3 4 5]) 
java.lang.Exception: Can't take value of a macro: #'user/square (NO_SOURCE_FILE:8) 

Pero envolviéndolo en una función hará el truco:

user=> (map #(square %) [1 2 3 4 5]) 
(1 4 9 16 25) 

Alternativamente (y un poco más malvado), podría hacer otra macro para hacer una envoltura más genérico:

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

user=> (map (make-fn square) [1 2 3 4 5]) 
(1 4 9 16 25) 

me pegaría con el envoltorio función normal, y evita este último truco! :)

+3

Hm, esto no funciona para las macros variadic: user => (apply # (or%) [true false false]) java.lang.IllegalArgumentException: Número incorrecto de args (3) pasados ​​a: user $ eval3 $ fn (NO_SOURCE_FILE: 0) – pauldoo

+2

3 puntos: (1) la mejor manera de resolver este ejemplo es '(alguna identidad [true false false])', (2) si conoce el número de params y lo hace explícito, es ' Hago el truco: '(aplicar # (o% 1% 2% 3) [true false false])', (3) el hacky 'make-fn' funciona:' (apply (make-fn or) [true false falso]) ' – trptcolin

5

Existe una macro obsoleta peligrosa que no debe usar nunca. :-P

http://clojure.github.com/clojure-contrib/apply-macro-api.html

+0

Hm, esto me da un desbordamiento de pila cuando intento este ejemplo en Clojure 1.2: (? Aplicarán-macro o [verdadero falso falso])) – pauldoo

+0

Uf es cierto, el problema es con la difusión función privada en el apply-macro ns: es una copia de un clojure.core obsoleto, si lo reemplaza con la versión real [el truco funciona] (https://gist.github.com/986321) – jneira

+0

El diff es curioso (resto [1]) ->(), (siguiente [1]) -> nil. El primero con el nil? como predicado de guardia para detener la recursión aumenta el stackoverflow. – jneira

4

Para la macro loco make-fn, ¿qué tal el siguiente? Debería funcionar también, y ojalá sea más fácil de entender.

(defmacro make-fn [m] 
`(fn [& args#] 
    (eval 
     (cons '~m args#)))) 
Cuestiones relacionadas