2012-06-11 12 views
5

Estoy intentando escribir una macro que construye un middleware similar al que se usa en compojure.Macro Clojure que crea una función de orden superior desde una función

quiero ser capaz de llamar a:

(def-middleware plus-two [x] 
    (+ 2 x)) 

Y tienen el resultado se parece a:

(defn plus-two [f] 
(fn [x] 
    (f (+ 2 x))) 

Tengo esta muy lejos de guías de lectura en línea pero no está funcionando para mí:

(defmacro def-middleware [fn-name args & body] 
    '(defn ~fn-name [f] 
    (fn ~args 
     (f [email protected])))) 

Cualquier ayuda o un puntero a una mejor guía de macro escritura sería genial, gracias.

+0

¿Cómo no funciona para usted? –

+0

CompilerException java.lang.RuntimeException: primer argumento para def debe ser un símbolo, compilando: (NO_SOURCE_PATH: 33) – jdoig

+0

usa la marca de retroceso, no la comilla simple – Alex

Respuesta

8

Veamos lo que nos da macroexpand-1:

user=> (clojure.pprint/pprint (macroexpand-1 '(def-middleware plus-two [x] (+ 2 x)))) 
(defn 
~fn-name 
[f] 
$ 
(fn ~args$ (f (clojure.core/unquote-splicing body)))) 

No es precisamente lo que estamos buscando! Lo primero es lo primero: si quieres anular la selección de elementos en una macro, necesitas usar "` "(el operador quasiquote/sintaxis-cita), no" '". Además, no estoy seguro de qué es lo que buscas con esos signos de dólar, pero podría que vas por el atajo práctico de clojure para usar gensym para la higiene. Pero lo necesita inmediatamente después de cada identificador con el que lo usa (así que [f#], no [f]$), y lo necesita en cada aparición del identificador. Y usted no lo necesita con args. Juntando todo esto:

user=> (defmacro def-middleware [fn-name args & body] `(defn ~fn-name [f#] (fn ~args (f# [email protected])))) 
#'user/def-middleware 
user=> (clojure.pprint/pprint (macroexpand-1 '(def-middleware plus-two [x] (+ 2 x)))) 
(clojure.core/defn 
plus-two 
[f__594__auto__] 
(clojure.core/fn [x] (f__594__auto__ (+ 2 x)))) 
nil 
user=> 
+0

Disculpe el $ donde forma copiar y pegar de vim. Y tienes razón, fue el # que me faltaba :) gracias. – jdoig

3

Tal vez esto es sólo un ejercicio de macro, pero el ejemplo específico de la definición de una función de middleware no es bueno - se pierde una gran cantidad de la flexibilidad que el concepto de middleware le da. Por ejemplo, ¿por qué debería esto evaluar (f (+ 2 x)) en lugar de (+ 2 (f x))? Ambas son formas sensatas de ajustar esta función, y su sintaxis no da ninguna forma de describir esta última. Realmente solo definir una función que devuelve una función es flexible, simple y fácil; esta macro trae poco a la mesa.

+0

Gracias amalloy; Lo estoy usando como un trampolín para aprender macro y también construir funciones de construcción similares a un middleware en lugar de este gran tonto que simplemente envuelve algo ... Como tienes razón, tal como está actualmente, este no puede causar un cortocircuito, etc. y no es descriptivo sobre cómo envuelve las cosas, etc. – jdoig

Cuestiones relacionadas