popping optional args from the front

15 views
Skip to first unread message

Stuart Halloway

unread,
Dec 19, 2009, 3:58:32 PM12/19/09
to clo...@googlegroups.com
In Clojure it is idiomatic to have optional args at the front of the
signature. This makes it easy to define convenient caller APIs, but it
leads to bulky let forms like this one (from clojure.core/defmulti)

(let [docstring (if (string? (first options))
(first options)
nil)
options (if (string? (first options))
(next options)
options)
m (if (map? (first options))
(first options)
{})
options (if (map? (first options))
(next options)
options)
dispatch-fn (first options)
options (next options)
m (assoc m :tag 'clojure.lang.MultiFn)
m (if docstring
(assoc m :doc docstring)
m)
m (if (meta mm-name)
(conj (meta mm-name) m)
m)]

Is it worth capturing this common idiom in a helper function, e.g. pop-
optional-args:

(defn pop-optional-args
[preds args]
(if (seq preds)
(if ((first preds) (first args))
(cons (first args) (pop-optional-args (rest preds) (rest args)))
(cons nil (pop-optional-args (rest preds) args)))
(list args)))

The above let form would then be:

(let [[docstring m dispatch-fn options] (pop-optional-args [string?
map? identity] options)
m (assoc m :tag 'clojure.lang.MultiFn)
m (if docstring
(assoc m :doc docstring)
m)
m (if (meta mm-name)
(conj (meta mm-name) m)
m)]

Worth doing? If so, how could it be better?

Stu

Sean Devlin

unread,
Dec 19, 2009, 5:26:12 PM12/19/09
to Clojure
Occasionally I have to write a custom def macro, and this would make
life easier. I would have to use it to provide specific feedback, but
it seems like an idea worth pursuing.

Rich Hickey

unread,
Dec 29, 2009, 1:15:25 PM12/29/09
to clo...@googlegroups.com

It's tough to guess as to the general applicability of this. One thing
for sure, it's not really about args, more like a 'take-' or 'split-'
variant. Finding a good name might point the way towards a general
utility.

Rich

Sean Devlin

unread,
Dec 29, 2009, 1:52:46 PM12/29/09
to Clojure
Hmmm... if we take a look at the first arglist...

[name doc-string? attr-map? [params*] body]

Each of this expects a different type of object right?

name - clojure.lang.Symbol
doc-string? - java.lang.String
attr-map? clojure.lang.IPeristentMap
params* - IPeristentVector
body - IPerisistentSeq

What if we used c.c.seq-utils/group-by with class? This would create
hash map with the appropriate values options in it. From there it's a
simple matter of processing a map. I think that the aritiy overloaded
version of defn is easy enough to detect to. Would that do the trick?

Sean

Konrad Hinsen

unread,
Dec 29, 2009, 4:42:14 PM12/29/09
to clo...@googlegroups.com
> On Sat, Dec 19, 2009 at 3:58 PM, Stuart Halloway
> <stuart....@gmail.com> wrote:
>> In Clojure it is idiomatic to have optional args at the front of the
>> signature. This makes it easy to define convenient caller APIs, but
>> it
>> leads to bulky let forms like this one (from clojure.core/defmulti)

There's clojure.contrib.def/name-with-attributes for the frequent case
of a def-ing macro that takes an optional docstring and/or attribute
map:

(defn name-with-attributes
"To be used in macro definitions.
Handles optional docstrings and attribute maps for a name to be
defined
in a list of macro arguments. If the first macro argument is a
string,
it is added as a docstring to name and removed from the macro
argument
list. If afterwards the first macro argument is a map, its entries
are
added to the name's metadata map and the map is removed from the
macro argument list. The return value is a vector containing the
name
with its extended metadata map and the list of unprocessed macro
arguments."
[name macro-args]
...)

Konrad.


Joost

unread,
Dec 29, 2009, 8:35:06 PM12/29/09
to Clojure
Personally, I prefer to use multiple implementations, like:

(defn aaa

Joost

unread,
Dec 29, 2009, 8:36:57 PM12/29/09
to Clojure
Personally, I prefer to use multple "prototypes":

(defn bla
([aaa bbb ccc] ....)
([bbb cc] (bla 0 bbb cc)))

etc.

Konrad Hinsen

unread,
Dec 30, 2009, 3:04:50 AM12/30/09
to clo...@googlegroups.com

That's the preferred approach for me too, but it doesn't work for
functions that take a variable number of arguments.

Konrad.

Reply all
Reply to author
Forward
0 new messages