Search code examples
clojurepartial

Partial source code clarification in Clojure


I'm a beginner in clojure trying to understand it at a basic level for now.

I've been experimenting with partial and how it creates closures, and to get a deeper understanding I thought I should take a peek at the source code by doing a (source partial) .

There I get

(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] f)
  ([f arg1]
   (fn
     ([] (f arg1))
     ([x] (f arg1 x))
     ([x y] (f arg1 x y))
     ([x y z] (f arg1 x y z))
     ([x y z & args] (apply f arg1 x y z args))))
  ([f arg1 arg2]
   (fn
     ([] (f arg1 arg2))
     ([x] (f arg1 arg2 x))
     ([x y] (f arg1 arg2 x y))
     ([x y z] (f arg1 arg2 x y z))
     ([x y z & args] (apply f arg1 arg2 x y z args))))
  ([f arg1 arg2 arg3]
   (fn
     ([] (f arg1 arg2 arg3))
     ([x] (f arg1 arg2 arg3 x))
     ([x y] (f arg1 arg2 arg3 x y))
     ([x y z] (f arg1 arg2 arg3 x y z))
     ([x y z & args] (apply f arg1 arg2 arg3 x y z args))))
  ([f arg1 arg2 arg3 & more]
   (fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))

I find the whole definition to be redundant as I would only write it in a "varargs" fashion, ie the last 2 lines.

Is this a readability feature or am I missing something basic here?


Solution

  • It's not a readability feature but a matter of performance. Stuart Sierra explained it in Clojure Don’ts: Optional Arguments with Varargs:

    Variable-arity function calls have to allocate a sequence to hold the arguments, then go through apply. Timothy Baldridge did a quick performance comparison showing that calls to a function with multiple, fixed arities can be much faster than variable-arity (varargs) function calls.

    In that benchmark, the varargs version with 1 argument is around an order of magnitude slower than the multi-arity version with 1 arg, and with 3 args the difference goes up to ~ 2 orders of magnitude.

    This is not to say that varargs should not be used at all: Sparsely calling a fn with varargs will probably not affect performance, but it could hit you hard when calling it from a tight loop.