2012-05-24 3 views
25

Я понимаю, что макрос -> in clojure применяет все функции, предоставленные аргументу. Однако он не работает с анонимными функциями (на clojure 1.3.0). Например:macro -> с анонимными функциями

user> (-> 4 inc inc dec) 
5 

Но:

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

Возвращает ошибку:

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

Если кто-то знает способ обойти это было бы полезно. Благодаря!

+3

Возможный дубликат [Function call in -> threading macro] (http://stackoverflow.com/questions/7838326/function-call-in-threading-macro) – amalloy

ответ

31

У вас могут быть анонимные функции в макросах Clojure. У вас проблемы, потому что вам не хватает скобок. :) Ваш пример отредактирован ниже.

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

Да, теперь это работает! Я боялся, что макрос -> не понравился анонимным функциям, но просто добавление parens делает трюк. Спасибо – S4M

+1

Откажитесь от 'macroexpand', чтобы точно знать, зачем ему нужны дополнительные парсеры для работы ... это интересный материал :) – Ankur

+0

Ankur: сделаю! Еще раз спасибо. – S4M

24

(это основано на answer на вопрос я писал в комментариях).

-> макро принимает каждый аргумент, что делает его список, если это необходимо (применяя «сырую» функцию в каких-либо аргументов - преобразование myfunc к (myfunc)), а затем вставляет первый аргумент -> в качестве второго аргумента в каждом из этих списков.

так (-> foo myfunc) становится (-> foo (myfunc)) становится (myfunc foo), примерно.

все это описано в docs for ->.

проблема с анонимными функциями заключается в том, что они генерируются макросом читателя как described here (scroll down). это означает, что #(...) преобразуется (до нормальное расширение макроса) в (fn [...] ...). что хорошо, но критически уже есть список.

поэтому макрос считает, что анонимная функция уже применяется, когда на самом деле она встречает определение функции (оба являются списками). и добавление «дополнительных» парен - как описано выше в другом ответе - применяет анонимную функцию к никаким аргументам.

Причина этого неинтуитивного поведения заключается в том, что dwim (do-what-i-mean, а не dwim-witted, хотя ...) эвристика, используемая макросом ->, добавлена, чтобы вы могли предоставить «голые "вместо того, чтобы требовать, чтобы вы применяли их к никаким аргументам, вставляя их в список, является только эвристический - он просто проверяет список - и путается с определением функции, созданным макросом читателя.

[по моему плохой оценке, -> плохо реализован и должен отклонять все «голые» функции, вместо этого принимать только приложения-функции; тогда он будет казаться более последовательным. если нет, то, по крайней мере, документы могут быть более ясными, объясняя мотивацию семантики за размещением вещей в списках.]

1

Ваш конкретный случай можно было решить просто с помощью:

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

где нить первый макрос -> заботится о вставке результат предыдущего шага в качестве первого аргумента в «текущей» функции.

Путаница возникает из-за того, что -> не является функцией, а макросом, и аргументы в этом случае рассматриваются по-разному, как объясняют другие ответы.

Смежные вопросы