Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I mix optional keyword arguments with the & rest stuff?

I have a macro that takes a body:

(defmacro blah [& body] (dostuffwithbody))

But I'd like to add an optional keyword argument to it as well, so when called it could look like either of these:

(blah :specialthingy 0 body morebody lotsofbody)
(blah body morebody lotsofboy)

How can I do that? Note that I'm using Clojure 1.2, so I'm also using the new optional keyword argument destructuring stuff. I naively tried to do this:

(defmacro blah [& {specialthingy :specialthingy} & body])

But obviously that didn't work out well. How can I accomplish this or something similar?

like image 640
Rayne Avatar asked May 02 '10 15:05

Rayne


Video Answer


1 Answers

Something like the following perhaps (also see how defn and some other macros in clojure.core are defined):

(defmacro blah [& maybe-option-and-body]
  (let [has-option (= :specialthingy (first maybe-option-and-body))
        option-val (if has-option (second maybe-option-and-body)) ; nil otherwise
        body (if has-option (nnext maybe-option-and-body) maybe-option-and-body)]
    ...))

Alternatively you could be more sophisticated; it might be worthwhile if you think you might want to have multiple possible options at some point:

(defn foo [& args]
  (let [aps (partition-all 2 args)
        [opts-and-vals ps] (split-with #(keyword? (first %)) aps)
        options (into {} (map vec opts-and-vals))
        positionals (reduce into [] ps)]
    [options positionals]))

(foo :a 1 :b 2 3 4 5)
; => [{:a 1, :b 2} [3 4 5]]
like image 134
Michał Marczyk Avatar answered Sep 24 '22 15:09

Michał Marczyk