Reputation: 198757
If I do the following:
user=> (-> ["1" "2"] (partial apply str))
#<core$partial__5034$fn__5040 clojure.core$partial__5034$fn__5040@d4dd758>
...I get a partial function back. However, if I bind it to a variable:
user=> (def apply-str (partial apply str))
#'user/apply-str
user=> (-> ["1" "2" "3"] apply-str)
"123"
...the code works as I intended it. I would assume that they are the same thing, but apparently that isn't the case. Can someone explain why this is to me?
Upvotes: 5
Views: 931
Reputation: 72946
The ->
macro adds parens around apply-str
in your second version, that's why the macro expands to code that ends up calling your function. Look at the source code for ->
and you can see:
(defmacro ->
"Threads the expr through the forms. Inserts x as the
second item in the first form, making a list of it if it is not a
list already. If there are more forms, inserts the first form as the
second item in second form, etc."
([x] x)
([x form] (if (seq? form)
(with-meta `(~(first form) ~x ~@(next form)) (meta form))
(list form x)))
([x form & more] `(-> (-> ~x ~form) ~@more)))
The relevant part is when it's dealing with two arguments, x
and form
. If form
is a seq, x
is inserted as the second argument in that list. Otherwise, the macro puts form
and x
it into a list itself. This is so you can use a bare symbol as shorthand for a list containing one symbol.
user> (macroexpand '(-> 123 (foo)))
(foo 123)
user> (macroexpand '(-> 123 foo))
(foo 123)
Upvotes: 0
Reputation: 19757
The Macro -> Threads the expr through the forms as second argument. In your case ends up in expanding to: (partial ["1" "2"] apply str)
, creating a parital function based on vector.
But you want to invoke a parital function based on apply and str on the threaded expr and thus need:
(-> ["1" "2"] ((partial apply str)))
Well: this code i quite confusing and not idiomatic Clojure.
Upvotes: 1
Reputation: 34850
The first expression, (-> ["1" "2"] (partial apply str))
, expands into:
(partial ["1" "2"] apply str)
which basically means:
Create a function from ["1" "2"]
(which is also a function, since vectors are functions of index keys!) with the Vars apply
and str
already supplied as the first two arguments. This function gets printed as the weird #<core$partial...>
string.
Only when this function will be called will you get an IllegalArgumentException since vectors only take one integer argument, not two Var arguments.
Upvotes: 4
Reputation: 32655
You don't have to do that at all.
(->> ["1" "2" "3"] (apply str))
Why not do that instead?
Upvotes: 5
Reputation: 1554
-> is a macro, so it doesn't have to follow the rules you would expect in terms of application. The macro transforms the source before the forms are evaluated. Try macroexpanding the forms:
user> (macroexpand '(-> ["1" "2"] (partial apply str)))
(partial ["1" "2"] apply str)
What are you trying to achieve here by using the '->' macro?
EDIT: Note that:
user> ((partial apply str) ["1" "2"])
"12"
Upvotes: 6