4

A lot of the Clojure functions accept various number of arguments and I'm often a bit confused by the documentation and how it relates to the way I should be using the function.

For example (doc partial) returns this:

([f arg1] [f arg1 arg2] [f arg1 arg2 arg3] [f arg1 arg2 arg3 & more])

My question is not specifically about partial but...

Why up to arg1 arg2 arg3 & more and not arg1 & more or arg1 arg2 & more or arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 & more ?

I'm being facetious with the last one but this is a very real question: what determines how many "argX" one needs to put before the & more?

Cedric Martin
  • 5,945
  • 4
  • 34
  • 66

2 Answers2

7

The following answer is a guess on my part: A look at the implementation of partial show us:

(defn partial
  "Takes a function f and fewer than the normal arguments to f, and
  returns a fn that takes a variable number of additional args. When
  called, the returned function calls f with args + additional args."
  {:added "1.0"
   :static true}
  ([f arg1]
   (fn [& args] (apply f arg1 args)))
  ([f arg1 arg2]
   (fn [& args] (apply f arg1 arg2 args)))
  ([f arg1 arg2 arg3]
   (fn [& args] (apply f arg1 arg2 arg3 args)))
  ([f arg1 arg2 arg3 & more]
   (fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))

As you can see, every call to partial is doing the same thing - namely, returning a function which accepts some args and calls apply on the input function with input args and new args. So this could indeed have been written as arg1 & more. But wait, lets look at the implementation of apply as well:

(defn apply
 "Applies fn f to the argument list formed by prepending intervening arguments to args."
 {:added "1.0"
  :static true}
 ([^clojure.lang.IFn f args]
    (. f (applyTo (seq args))))
 ([^clojure.lang.IFn f x args]
    (. f (applyTo (list* x args))))
 ([^clojure.lang.IFn f x y args]
    (. f (applyTo (list* x y args))))
 ([^clojure.lang.IFn f x y z args]
    (. f (applyTo (list* x y z args))))
 ([^clojure.lang.IFn f a b c d & args]
    (. f (applyTo (cons a (cons b (cons c (cons d (spread args)))))))))

Apply is a core function, and it is executes differently when given a different number of arguments. This is an optimization to apply for performance reasons. This is the reason for exposing different arities of partial (and other such functions), because the internal execution of the code differs for different arities.

I'm assuming that the clojure/core team thought that exposing the arity of partial beyond arg1 arg2 arg3 & more (ie writing arg1 arg2 arg3 arg4 & more) would not be aesthetic, so they decided to stop at 3 args & more.

vedang
  • 3,111
  • 2
  • 24
  • 33
3

Whatever you think is reasonable. Its going to be faster to call a function if its not of variable arity, so they provide several that will be called directly if you don't exceed their limit.

I think there's also a limit in IFn of 20(?) args to a function, but I don't know if there's a work around in the compiler for that or not.

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
  • 20 isn't a limit in `IFn`. If you want more than 20 arguments there's a varargs method. See the [IFn source](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java). – Andrew May 26 '12 at 21:02
  • @Bill: oh I see... +1 to bth answer (they explain basically the same). I'll accept the other one because user *vedang* has less rep ; ) – Cedric Martin May 26 '12 at 22:29
  • @Andrew Right, I meant that the limit for fixed is 20 (?), after that yes there's the varargs, and what I was wondering about is if you create a function with 25 args, will the compiler automatically do the varargs call for you? – BillRobertson42 May 26 '12 at 23:10