the following is a macro defining a function with keyword
arguments. It is called like this:
(defnk foo
[a b c :x 1 :z 2]
(println x z))
a, b and c denote positional arguments, 1 and 2 are default
values for x resp. z. This pattern came up several times now,
and there were always some "aha" effects. So it may be a
nice addition to c.c.def.
Here's the macro:
(defmacro defnk
"Define a function accepting keyword arguments. Symbols up to the
first
keyword in the parameter list are taken as positional arguments.
Then
an alternating sequence of keywords and defaults values is
expected. The
values of the keyword arguments are available in the function body by
virtue of the symbol corresponding to the keyword (cf. :keys
destructuring).
defnk accepts an optional docstring as well as an optional metadata
map."
[fn-name & fn-tail]
(let [[fn-name [args & body]] (name-with-attributes fn-name fn-tail)
[pos kw-vals] (split-with symbol? args)
kw-vals (apply hash-map kw-vals)
de-map {:keys (vec (keys kw-vals))
:or kw-vals}]
`(defn ~fn-name
[~@pos & options#]
(let [~de-map (apply hash-map options#)]
~@body))))
The docstring is a bit contorted but I'm too sleepy now,
to get that right...
Sincerely
Meikel
Am 05.06.2009 um 00:06 schrieb Meikel Brandmeyer:
> The docstring is a bit contorted but I'm too sleepy now,
> to get that right...
And of course I'm too sleepy to miss the keyword to
symbol conversion....
(defmacro defnk
"Define a function accepting keyword arguments. Symbols up to the
first
keyword in the parameter list are taken as positional arguments.
Then
an alternating sequence of keywords and defaults values is
expected. The
values of the keyword arguments are available in the function body by
virtue of the symbol corresponding to the keyword (cf. :keys
destructuring).
defnk accepts an optional docstring as well as an optional metadata
map."
[fn-name & fn-tail]
(let [[fn-name [args & body]] (name-with-attributes fn-name fn-tail)
[pos kw-vals] (split-with symbol? args)
syms (map #(-> % name symbol) (take-nth 2
kw-vals))
values (take-nth 2 (rest kw-vals))
sym-vals (apply hash-map (interleave syms
values))
de-map {:keys (vec syms)
:or sym-vals}]
`(defn ~fn-name
[~@pos & options#]
(let [~de-map (apply hash-map options#)]
~@body))))
Sincerely
Meikel
> Gut gemacht!
>
> Absolutely amazing Meikel. Now get some well earned sleep.
>
> Sean
I agree. It's a really beautiful piece of code, packed full of Clojure
goodness.
Nicely done!
I checked it into clojure.contrib.def:
http://code.google.com/p/clojure-contrib/source/detail?r=889
Thanks!
--Steve
Am 05.06.2009 um 00:22 schrieb Meikel Brandmeyer:
> sym-vals (apply hash-map (interleave syms
> values))
Ah! I always forget about zipmap...
Clojure is just fun! :)
Sincerely
Meikel