views:

199

answers:

5

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?

+5  A: 

-> 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"
T Duncan Smith
The code that I'm trying to write is somewhat more complex than that. This is just a simplified example. I just want to get a better understanding of how the `->` macro works. :-)
Jason Baker
Ah, gotcha. Anyway, you see what's going on here?
T Duncan Smith
Yes I do. Thanks for your answer!
Jason Baker
+3  A: 

You don't have to do that at all.

(->> ["1" "2" "3"] (apply str))

Why not do that instead?

Rayne
+3  A: 

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.

Michiel Borkent
please note that Clojure being dynamic the fact that vectors are functions doesn't matter for this bug. (partial "hello" "world") returns a function too -- a function which always throws though.
cgrand
A: 

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)
Brian Carper
+1  A: 

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.

Jürgen Hötzel