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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With