2012-05-24 15 views
25

Entiendo que la macro -> en clojure está aplicando todas las funciones proporcionadas al argumento dado. Sin embargo, no parece funcionar con funciones anónimas (en clojure 1.3.0). Por ejemplo:macro -> con funciones anónimas

user> (-> 4 inc inc dec) 
5 

Pero:

user> (-> 4 #(+ % 1) #(- % 1) #(+ % 1)) 

devuelve el error:

clojure.lang.Symbol cannot be cast to clojure.lang.IPersistentVector 
[Thrown class java.lang.ClassCastException] 

Si alguien conoce una manera de evitarlo sería de gran ayuda. ¡Gracias!

+3

posible duplicado de [Llamada de función en -> macro de roscado] (http://stackoverflow.com/questions/7838326/function-call-in-threading-macro) – amalloy

Respuesta

31

Puede tener funciones anónimas en las macros de Clojure. Estás teniendo problemas, porque te faltan algunos paréntesis. :) Tu ejemplo está editado a continuación.

(-> 4 (#(+ % 1)) (#(- % 1)) (#(+ % 1))) 
+0

Sí, funciona ahora! Temía que a la macro -> no le gustaran las funciones anónimas, pero simplemente agregar los parens es lo que hace falta. Gracias – S4M

+1

Echa un vistazo a 'macroexpand' para saber exactamente por qué necesita paréntesis adicionales para trabajar ... es algo interesante :) – Ankur

+0

Ankur: ¡haré! Gracias de nuevo. – S4M

24

(esto se basa en la answer a la pregunta que ha escrito en los comentarios).

la -> macro toma cada argumento, lo que es una lista si es necesario (la aplicación de funciones "en bruto" a no hay args - Convertir myfunc-(myfunc)), y luego inserta el primer argumento de -> como segundo argumento en cada una de esas listas.

así (-> foo myfunc) se convierte en (-> foo (myfunc)) se convierte en (myfunc foo), aproximadamente.

esto se describe en el docs for ->.

el problema con las funciones anónimas es que son generadas por una macro de lector como described here (scroll down). eso significa que #(...) se convierte (antes de macro expansión normal) en (fn [...] ...). lo cual está bien, pero, críticamente, ya es una lista.

por lo que la macro cree que la función anónima ya se está aplicando, cuando en realidad se encuentra con una definición de función (ambas son listas). y al agregar los parens "adicionales", como se describió anteriormente en la otra respuesta, se aplica la función anónima a ningún argumento.

el motivo de este comportamiento poco intuitivo es que la heurística dwim (do-what-i-mean, no dwim-witted, aunque ...) utilizada por la macro ->, agregada para permitirle proporcionar "bare "Las funciones en lugar de requerir que las aplique a ningún argumento incluyéndolas en una lista, es solo una heurística; simplemente prueba una lista y se confunde con la definición de la función creada por la macro del lector.

[en mi mala opinión, -> está mal implementado y en su lugar debe rechazar todas las funciones "simples", en cambio, solo acepta aplicaciones de funciones; entonces parecería más consistente. si no, al menos los documentos podrían ser más claros, explicando la semántica motivadora detrás de colocar cosas en las listas.]

1

su caso específico podría haberse resuelto el simple uso:

(-> 4 (+ 1) (- 1) (+ 1)) 

donde el hilo primera macro -> se encarga de insertar el resultado del paso anterior como el primer argumento de la función "actual".

La confusión surge del hecho de que -> no es una función sino una macro y los argumentos se tratan de manera muy diferente en este caso, como se explica en otras respuestas.

Cuestiones relacionadas