10

Examples of Clojure arity-overloading on functions like the following (taken from the cookbook):

(defn argcount
  ([] 0)                                ; Zero arguments
  ([x] 1)                               ; One argument
  ([ x &  args] (inc (count args))))    ; List of arguments

... use a form that doesn't seem to allow the functions of lower arity to simply call the functions of higher arity with some default values (that's a common idiom in Java). Is some other special form used for that ?

Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
  • 3
    There are two ways to do this, and this question has already been answered on StackOverflow. Pay special attention to the second, later answer here: http://stackoverflow.com/a/8660833/152739 – Scott Feb 09 '13 at 23:20
  • Seems to work fine for me: user=> (defn f ([] (f 1)) ([x] x)) #'user/f user=> (f) 1 – Brandon Bloom Apr 23 '14 at 03:24

2 Answers2

14

There's usually a good way to express the higher arity arguments in a way that doesn't need to refer to other arities using higher order functions and map / reduce. In this case it's pretty simple:

(defn argcount
  ([] 0)
  ([x] 1)
  ([x & args]
    (reduce + 1 (map (constantly 1) args))))

Notice the general form of the expression is:

(reduce reducing-function arity-1-value (map mapping-function rest-of-args))

You can't do everything this way, but this works for a surprisingly large proportion of multi-argument functions. It also gains the advnatages of laziness using map, so you can do crazy things like pass ten million arguments to a function with little fear:

(apply argcount (take 10000000 (range)))
=> 10000000

Try that in most other languages and your stack will be toast :-)

mikera
  • 105,238
  • 25
  • 256
  • 415
3

mikera's answer is awesome; I'd just add an additional method. When the a default value is needed for an overloaded function, a local can be used.

In the example division below, the local requires numbers and precision. The defined function overloads the precision with a default value.

(def overloaded-division
 (let [divide-with-precision
  (fn [divisor dividend precision]
    (with-precision precision (/ (bigdec divisor) (bigdec dividend))))]
     (fn
      ;lower-arity calls higher with a default precision.
      ([divisor dividend] (divide-with-precision divisor dividend 10))
      ;if precision is supplied it is used.
      ([divisor dividend precision] (divide-with-precision divisor dividend precision)))
   )
 )

When called at lower-arity, the default it applied:

user=> (overloaded-division 3 7)
0.4285714286M
user=> (overloaded-division 3 7 40)
0.4285714285714285714285714285714285714286M
alexgibbs
  • 2,430
  • 2
  • 16
  • 18