Johan
Johan

Reputation: 40500

Is there a workaround for multi arity functions with more than 1 variadic overload in Clojure?

I've created a macro whose definition looks like this:

(defmacro my-macro
  [strategy & body] 
   ...)

You can call this macro like this:

(my-macro {:strategy "some"}
       (do-something ..))

Now I'd like the macro to optionally accept bindings. Something like this:

(my-macro [my-value (load-value ..)]
       {:strategy my-value}
       (do-something ..))

So I've tried extending the macro to look like this:

(defmacro my-macro
  ([strategy & body] `(my-macro [] ~routes ~body))
  ([bindings strategy & body] 
    ...))

But this fails with:

java.lang.RuntimeException: Can't have more than 1 variadic overload

Is there a good way to workaround this or what's the recommended approach?

Upvotes: 2

Views: 876

Answers (2)

leetwinski
leetwinski

Reputation: 17859

i would propose this one:

(defmacro my-macro
  {:arglists '([bindings? strategy & body])}
  [bindings-or-strategy & more-args]
    (let [[bindings strategy body] (if (vector? bindings-or-strategy)
                                     [bindings-or-strategy
                                      (first more-args)
                                      (rest more-args)]
                                     [[] bindings-or-strategy more-args])]
      `(let ~bindings
         (println :strategy ~strategy)
         ~@body)))

user> (my-macro [a 1] {:aaa :bbb} a)
:strategy {:aaa :bbb}
1
user> (my-macro {:aaa :bbb} 10)
:strategy {:aaa :bbb}
10

although it looks more verbose than @superkonduktr's answer, to me it seems to be more usable: it ensures that at least one argument is passed, and also documents the usage of this macro with :arglists

Upvotes: 3

superkonduktr
superkonduktr

Reputation: 655

My typical workaround would be to just examine the entire list of arguments passed to my-macro and decide whether to use bindings or not:

(defmacro my-macro
  [& body]
  (let [bindings (if (vector? (first body)) (first body) [])
        [strategy body*] (if (seq bindings) (rest body) body)]
    `(let ~bindings
       (do-something-with ~body* ~strategy))))

Upvotes: 4

Related Questions