I understand the `-> theading macro in Clojure applies all the provided functions provided to a given argument. However, it doesn't seem to work with anonymous functions. For example:
user> (-> 4 inc inc dec)
5
But:
user> (-> 4 #(+ % 1) #(- % 1) #(+ % 1))
Returns the error:
clojure.lang.Symbol cannot be cast to clojure.lang.IPersistentVector
[Thrown class java.lang.ClassCastException]
If someone knows a way around it would be helpful. Thanks!
You can have anonymous functions in Clojure macros. You are having problems, because you are missing some parentheses. :) Your example is edited below.
(this is based on the answer to the question i posted in comments).
the
->
macro takes each argument, making it a list if necessary (applying "raw" functions to no args - convertingmyfunc
to(myfunc)
), and then inserts the first argument to->
as second argument in each of those lists.so
(-> foo myfunc)
becomes(-> foo (myfunc))
becomes(myfunc foo)
, roughly.this is all described in the docs for
->
.the problem with anonymous functions is that they are generated by a reader macro as described here (scroll down). that means that
#(...)
is converted (before normal macro expansion) into(fn [...] ...)
. which is fine, but, critically, is already a list.so the macro believes that the anonymous function is already being applied, when in fact it is encountering a function definition (both are lists). and adding the "extra" parens - as described above in the other answer - applies the anonymous function to no args.
the reason for this un-intuitive behaviour is that the dwim (do-what-i-mean, not dwim-witted, although...) heuristic used by the
->
macro, added to allow you to supply "bare" functions rather than requiring that you apply them to no args by enclosing them in a list, is just a heuristic - it simply tests for a list - and is confused by the function definition created by the reader macro.[in my bad tempered opinion,
->
is poorly implemented and should instead reject all "bare" functions, instead only accepting function applications; it would then appear more consistent. if not, then at least the docs could be clearer, explaining the motivating semantics behind placing things in lists.]Your specific case could have been solved simply using:
where the thread first macro
->
takes care of inserting the result of the previous step as the first argument in the "current" function.The confusion arises from the fact that
->
is not a function but a macro and the arguments are treated very differently in this case as explained by other answers.