:keys and :or destructuring where defaults refer to one another

101 views
Skip to first unread message

Michael Blume

unread,
Dec 11, 2014, 6:11:59 PM12/11/14
to clo...@googlegroups.com
If I make my defaults on a :keys :or destructuring depend on the values of other keys, I *can* get a compile-time error, depending on what order I give the keys https://gist.github.com/MichaelBlume/4891dafdd31f0dcbc727

Is this on-spec behavior? Should the former be allowed but not the latter? Should both be allowed? Should neither?

Christopher Small

unread,
Dec 11, 2014, 9:37:13 PM12/11/14
to clo...@googlegroups.com
I wouldn't have expected either form to work actually; It's interesting that the first does. I would love someone else enlighten us on this, but my guess is that it's something not specified and that you shouldn't rely on.

If you want to have optional argument defaults depend on each other, I recommend using a separate let form as shown in my answer on stack overflow (I'm metasoarous).

Best

Chris

adrian...@mail.yu.edu

unread,
Dec 11, 2014, 9:56:17 PM12/11/14
to clo...@googlegroups.com
Whenever you want to get insight in how a macro is rewriting your code, try evaluating your form quoted with macroexpand. 

Here's a gist with the macroexpansion each form. 


Hopefully the expansion makes things clear! 

Michael Blume

unread,
Dec 11, 2014, 11:55:36 PM12/11/14
to clo...@googlegroups.com, cloju...@googlegroups.com
Yep, I spent some time playing with the macro and the macroexpand. It looks like

a) it only works if the dependent keys come *before* the keys they depend on (ie the opposite of how you'd order, say, defs)

b) this ordering arises entirely from the seq ordering of PersistentArrayMap (keys are stuck into the map here https://github.com/clojure/clojure/blob/clojure-1.6.0/src/clj/clojure/core.clj#L4083 and taken out again here https://github.com/clojure/clojure/blob/clojure-1.6.0/src/clj/clojure/core.clj#L4090)

The latter makes it pretty clear that this is accidental behavior and, as you say, shouldn't be relied on -- in particular, if you have more than about 8 keys, you spill over to a PersistentHashMap and get everything in a random order and it almost certainly fails.

The trouble is this behavior is already used by ring-middleware-format, which then fails to compile if clojure uses a different implementation for small maps.

I'm wondering if given the brittleness of this behavior we should make sure it can't be used in future versions of clojure.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@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+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

adrian...@mail.yu.edu

unread,
Dec 12, 2014, 12:44:57 AM12/12/14
to clo...@googlegroups.com, cloju...@googlegroups.com
Common Lisp has a really well thought approach to parameter lambda lists. If you feel strongly about resolving this ambiguity and enforcing consistency around sequential binding in the destructuring syntax, perhaps that would be a good place to root a design you can flesh out in Jira. Here's a resource you might find interesting as a starting point: http://www.lispworks.com/documentation/HyperSpec/Body/03_dad.htm

Michał Marczyk

unread,
Dec 12, 2014, 2:35:39 AM12/12/14
to clojure, cloju...@googlegroups.com
Quoting Michael's Gist:

(let [{:keys [bar foo]
:or {foo 1
bar (inc foo)}} {}]
(assert (= foo 1))
(assert (= bar 2)))

The main problem I see with this is that I'd expect the foo in :or to
refer to any foo in the enclosing scope. As things stand, whether it
does or not depends on the ordering of keys in :keys:

(let [foo 1
bar 2
{:keys [bar foo]
:or {foo 3 bar (inc foo)}} {}]
{:foo foo :bar bar})
;= {:foo 3, :bar 4}

(let [foo 1
bar 2
{:keys [foo bar]
:or {foo 3 bar (inc foo)}} {}]
{:foo foo :bar bar})
;= {:foo 3, :bar 2}

Feels like a bug to me.

Cheers,
Michał

Michał Marczyk

unread,
Dec 12, 2014, 3:20:13 AM12/12/14
to clojure, cloju...@googlegroups.com
I created a ticket and posted a patch:

http://dev.clojure.org/jira/browse/CLJ-1613
Reply all
Reply to author
Forward
0 new messages