1

In JavaScript, one can do the following:

function foo(arg1, arg2, arg3) { ... } var others = [ 'two', 'three' ]; foo('one', ...others); // same as foo('one', 'two', 'three') 

In Clojure, "variable args" can be accepted like so:

(defn foo [arg1 & others] ...) 

But to pass them in combination with other args, you have to do this:

(apply foo (concat '("one") others)) 

Which is frankly really ugly. It's also impossible when what you need to do is recur:

(apply recur (concat '("one") others)) ;; doesn't work 

Is there a better way to do this? And if not, is there any way at all to accomplish it in the recur case?

1 Answer 1

5

But to pass them in combination with other args, you have to do this: (apply foo (concat '("one") others))

You don't have to do that: apply is also a variadic function that can take arguments before the final sequence argument e.g.

(apply foo "one" others) 

You can pass any number of individual arguments before the final sequence argument to apply.

user=> (defn foo [arg1 & args] (apply println arg1 args)) #'user/foo user=> (apply foo "one" 2 "three" [4 "five" 6.0]) one 2 three 4 five 6.0 

To further demonstrate, these calls to + are functionally equivalent:

(apply + 1 2 [3]) (apply + 1 [2 3]) (apply + [1 2 3]) 

It's also impossible when what you need to do is recur

recur is a special form, and apply doesn't work with it like it would a typical Clojure function.

is there any way at all to accomplish it in the recur case?

Not with apply. You can recur with variadic args, but you can't (apply recur ...).

Sign up to request clarification or add additional context in comments.

7 Comments

"You don't have to do that: apply is also a variadic function that can take arguments before the final sequence argument e.g. (apply foo "one" others) You can pass any number of individual arguments before the final sequence argument to apply." But wouldn't others in this case be passed as a list, and not have its members "flattened" into the remaining arg slots?
@brundolf in that case others would have its members flattened/spread across the remaining argument positions. (apply + 1 2 [3 4]) is equivalent to (apply + 1 [2 3 4]) and (apply + [1 2 3 4]).
Odd. What's the exact algorithm there? Surely it can't be, "flatten every sequence passed to apply"? "Flatten the last argument if it's a sequence", perhaps?
@brundolf It basically assumes that the last parameter is a list. Any other arguments before it and after the function argument are treated as elements to to consd to the argument list.
It's perfectly possible with recur. Given (defn f [x & xs] ...), there are two variables that need to be passed to any recur call: x and xs. You don't need the apply, because the compiler knows exactly which arity is being dispatched to (the current one). Thus: (defn f [x & xs] (if foo 0, (recur (inc x) (rest xs)))(
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.