This is exactly the approach to take: a macro which expands to a defn with 
all your arities filled out.

Here's a simple approach which might be enough for your problem: it will 
splice in argument names whenever some marker symbol is encountered, and 
repeat for the range of arities you want.


(defn splice [marker replacement form]
  (cond
    (seq? form) (reduce #(if (= marker %2)
                          (into %1 (reverse replacement))
                          (conj %1 (splice marker replacement %2)))
                  () (reverse form))
    
    (or (vector? form) (set? form))
    (reduce #(if (= marker %2)
              (into %1 replacement)
              (conj %1 (splice marker replacement %2)))
      (empty form) form)
    
    (map? form)
    (reduce-kv #(assoc %1 (splice marker replacement %2)
                          (splice marker replacement %3)) {} form)
    
    (= marker form)
    (throw (ex-info "Cannot splice into a top-level form!" {}))
    
    :else form))

(defmacro defvariadic
;; Full support for defn options (attr-map, docstring, pre/post conditions)
;; an exercise for the reader.
  [name marker max-arity params & body]
  (assert (and (symbol? name) (nil? (namespace name))))
  (assert (and (symbol? marker)))
  (assert (and (integer? max-arity) (<= 0 max-arity 24)))
  (assert (vector? params))
  (let [arg-syms (mapv gensym (subs "abcdefghijklmnopqrstuvwxyz" 0 
max-arity))
        params+bodies
                 (map (fn [arity]
                        (let [var-params (subvec arg-syms 0 arity)]
                          (list* (splice marker var-params params)
                            (splice marker var-params body))))
                   (range max-arity))]
    `(defn ~name ~@params+bodies)))


This produces output like the following:

(clojure.pprint/pprint (macroexpand-1
  '(defvariadic default-ontology-dispatch <> 5
    [f <>]
    (dispatch f <>))))


(clojure.core/defn
 default-ontology-dispatch
 ([f] (dispatch f))
 ([f a23733] (dispatch f a23733))
 ([f a23733 b23734] (dispatch f a23733 b23734))
 ([f a23733 b23734 c23735] (dispatch f a23733 b23734 c23735))
 ([f a23733 b23734 c23735 d23736]
  (dispatch f a23733 b23734 c23735 d23736)))






On Thursday, June 4, 2015 at 11:55:23 AM UTC-5, Phillip Lord wrote:
>
>
> I have a number of fairly nasty functions with a form that looks like 
> this: 
>
> (defn default-ontology 
>   ([f] 
>      (dispatch f)) 
>   ([f a] 
>      (dispatch f a)) 
>   ([f a b] 
>      (dispatch f a b)) 
>   ([f a b c] 
>      (dispatch f a b c)) 
>   ([f a b c d] 
>      (dispatch f a b c d)) 
>   ([f a b c d e] 
>      (dispatch f a b c d e)) 
>   ([f a b c d e fa] 
>      (dispatch f a b c d e fa)) 
>   ([f a b c d e fa g] 
>      (dispatch f a b c d e fa g)) 
>   ([f a b c d e fa g h] 
>      (dispatch f a b c d e fa g h)) 
>   ([f a b c d e fa g h i] 
>      (dispatch f a b c d e fa g h i)) 
>   ([f a b c d e fa g h i j] 
>      (dispatch f a b c d e fa g h i j))) 
>
> The reason for all of this is that I need to avoid the use of variadic 
> function calls for performance reasons -- this function gets called a 
> lot in my code base, and without this unwinding, I box and unbox 
> consistantly. 
>
> Now, I dislike the code repetition here, and indeed have found already 
> found one bug in my code where I missed a variable out, something like.... 
>
> ([f a b c d] 
>  (dispatch f a b d)) 
>
> which is hard to pick up on. 
>
> I can't make the whole thing a macro because I need to pass this as a 
> first class function. And I can't macro each of the variadic elements 
> since the I'd need to return two elements at once. 
>
> The best I have done up with so far is: 
>
> (defn ^:private form-with-arity[n] 
>   ;; left as an exercise for the reader     
> ) 
>
> (defmacro ^:private m-default-ontology 
>   `(defn default-ontology 
>       ~@(map form-with-arity (range 1 10)))) 
>
> (m-default-ontology) 
>
> Or am I missing something more obvious? 
>
> Phil 
>
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to