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.
(-> 4 (#(+ % 1)) (#(- % 1)) (#(+ % 1)))
(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 - converting myfunc
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:
(-> 4 (+ 1) (- 1) (+ 1))
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.