Possible to merge destructuring :or defaults with :as?

122 views
Skip to first unread message

Peter Taoussanis

unread,
Aug 31, 2012, 6:40:04 AM8/31/12
to clo...@googlegroups.com
Hey all,

As currently implemented:

(defn foo
  [& {:keys [x y z] :or {x "x-default" y "y-default"} :as args}]
  args)

(foo) => nil

Is there a way anyone can think of (macro, etc.) that'd allow for something like this:

(defn foo
  [& {:keys [x y z] :or {x "x-default" y "y-default"} :merge-as args}]
  args)

(foo) => {:x "x-default" :y "y-default}

I.e. to merge the :or defaults over the :args binding?

Rationale:  one of the nice things of setting defaults via :or is that they're visible to callers via :arglists. This'd help make the process more convenient in the (relatively common) case where you'd like to preserve arbitrary args, but apply some defaults in a transparent way.

Thanks!

- Peter Taoussanis

Stephen Compall

unread,
Sep 2, 2012, 6:10:01 PM9/2/12
to clo...@googlegroups.com
On Fri, 2012-08-31 at 03:40 -0700, Peter Taoussanis wrote:
> (foo) => {:x "x-default" :y "y-default}
>
>
> I.e. to merge the :or defaults over the :args binding?

Such behavior would be quite surprising; to see why, desugar :keys:

{x (expr-that-computes :x),
y (expr-that-computes :y),
z (expr-that-computes :z),
:or {x "x-default" y "y-default"} :merge-as args}

which treats bindings as having bidirectional flow; we bind a name to
the value of a keyword, rewriting in the other direction if the value
isn't present.

Instead,

> Rationale: one of the nice things of setting defaults via :or is that
> they're visible to callers via :arglists. This'd help make the process
> more convenient in the (relatively common) case where you'd like to
> preserve arbitrary args, but apply some defaults in a transparent way.

When you want this,

(let [{blah} (merge my-defaults kwargs)]

--
Stephen Compall
"^aCollection allSatisfy: [:each | aCondition]": less is better than


Peter Taoussanis

unread,
Sep 3, 2012, 2:34:20 AM9/3/12
to clo...@googlegroups.com
Hi Stephen,

Thanks for the reply!

Such behavior would be quite surprising; to see why, desugar :keys:

I may well be missing something, but I'm not sure I see your problem here.

(defn foo [& {x :x y :y :as args :or {x "x-default" y "y-default"}}]
  args)

This is checking input args for an :x keyword. If found, it's binding the associated value to 'x. Else it's binding the default value to 'x.

Either way, it's also binding all the args as originally given to 'args.

So with (foo :y "boo!") we have at this point:

* All args as originally given, bound to 'args. So '(:y "boo!")
* A map of symbols to default values. So {x "x-default" y "y-default"}
* Bindings as a result of the destructuring. So [x "x-default" y "boo!"]

What I'm suggesting might be useful, is destructuring sugar to bind one additional map: {:x "x-default" :y "boo!"}.

The clojure.core/destructure code is pretty hairy, so I may be missing some implementation-specific reason why this would be difficult to do. But I don't see anything logically inconsistent or surprising about wanting to do it. It'd be just another binding, and so not outside the scope of what destructure is tasked with doing.

Indeed, I find real-world situations coming up quite often where this would be a useful convenience.

When you want this,

(let [{blah} (merge my-defaults kwargs)]

Unfortunately (again, unless I'm missing something) this fails on two counts. One, it loses the transparency of the defaults to callers (the original motivation I described). Two, it loses the convenience that :or gives of actually having the symbols automatically bound as well.

I.e. to really match the functionality I'm talking about, you'd need to get more verbose:

(defn foo [& {:keys [x y] :as args}]
  (let [merged-args (merge {:x "x-default"} args)
        {:keys [x y]} merged-args]
    merged-args))

And, indeed, the repetition in this form makes it clear (I think) why this would ideally be the domain of the destructuring itself.


Does that make sense?

--
Peter Taoussanis
ptaou...@gmail.com

Gunnar Völkel

unread,
Sep 3, 2012, 3:40:06 AM9/3/12
to clo...@googlegroups.com
In case you are primarily interested in clojure functions with keyword arguments (or "optional arguments"), you might check if clojure.options (https://github.com/guv/clojure.options/) suits you.
Reply all
Reply to author
Forward
0 new messages