Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure :: arity-overloaded functions calling each other

Tags:

clojure

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 ?

like image 203
Marcus Junius Brutus Avatar asked Feb 09 '13 22:02

Marcus Junius Brutus


2 Answers

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 :-)

like image 178
mikera Avatar answered Nov 17 '22 03:11

mikera


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
like image 27
alexgibbs Avatar answered Nov 17 '22 02:11

alexgibbs