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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.