Jason Baker
Jason Baker

Reputation: 198757

(partial apply str) and apply-str in clojure's ->

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

Answers (5)

Brian Carper
Brian Carper

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

J&#252;rgen H&#246;tzel
J&#252;rgen H&#246;tzel

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

Michiel Borkent
Michiel Borkent

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

Rayne
Rayne

Reputation: 32655

You don't have to do that at all.

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

Why not do that instead?

Upvotes: 5

Tagore Smith
Tagore Smith

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

Related Questions