On Sat, Jan 29, 2011 at 5:18 PM, Robert McIntyre <r...@mit.edu> wrote: > I see, so you wanted to allow some subset of the optional arguments. > > Might I recommend the following? > Sets are already functions that check for inclusion of the objects on > which you call them, so instead of (contains? some-set ele) just > (some-set ele) will work. Also, upon actually making a typo you just > get the statement that (contains? #{:a :b} each) has failed, while I > think it would be nicer if it actually showed the offending misspelled > keyword. Unfortunately implementing this proved harder than I expected > and the best I could come up with was: > > (defn hello > [& {:keys [a b] :as input}] > (dorun (map #(eval `(assert (#{:a :b} (quote ~%)))) > (keys input))) > "hello") > > I'd be curious what the right way to to this is.
A macro. (defmacro defkfn [name keys & body] (let [[docstring keys body] (if (string? keys) [[keys] (first body) (rest body)] [[] keys body])] `(defn ~name ~@docstring [& {:keys ~keys :as input#}] (doseq [k# (keys input#)] (assert (~(set (map (comp keyword str) keys)) k#))) ~@body))) user=> (defkfn foo "docstring" [a b] (+ a (* 2 b))) #'user/foo user=> (doc foo) ------------------------- user/foo ([& {:as input__4130__auto__, :keys [a b]}]) docstring nil user=> (foo :a 1 :b 2) 5 user=> (foo :a 2 :b 1) 4 user=> (foo :a 2 :b 1 :c 3) #<CompilerException java.lang.AssertionError: Assert failed: (#{:a :b} k__4131__auto__) (NO_SOURCE_FILE:501)> Docstring optional. Sorry about the ugly argument list in the doc output. That can't be easily changed without adding code to the macro to fiddle with the var's meta. :) This suffices to show how to make this sort of thing work, though. Note that it basically just automates outputting code like yours, Robert, minus the eval which can be dispensed with. (doseq [k (keys input)] (assert ({:a :b} k))) works fine; there's nothing magical about assert that stops you using it in such a manner. Modifying this to allow supplying optional default values for the keys is left as an exercise for the reader. I suggest a syntax like {:keys a-key [another-key default-value-expr] more-keys}. The macro will need to parse this in the let part and build a map of keywords to default values, and avoid multiply evaluating default-value-exprs. The actual output part of the macro, the syntax-quoted expression, will need changing as well to merge the defaults with the input. I'd suggest the output should probably look like this: (let [default-map__1082_auto] {:another-key default-value}) (defn foo [arg-map__1083_auto] (let [{:keys [keys...] :as input__1084_auto} (merge default-map__1082_auto arg-map__1083_auto)] (doseq [k__1085_auto input__1084_auto] (assert ...)) body...))) -- 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