You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Since clojure 1.11 (as per CLJ-2603), destructuring a sequence containing a single map can directly bind to the map contents. An example of this can be seen here. It might be a good idea to mention this in the destructuring guide as this behaviour might be surprising to some (at least it was for me).
daveliepmann, eerohele, onetom, NoahTheDuke, alexander-yakushev and 2 more
when called as (configure 12 (list :debug true)) or (configure 12 (list {:debug true})),
but not equivalent, when called as (configure 12 (vector :debug true)) or (configure 12 (vector {:debug true})).
im not sure what reasoning can be given for this behaviour though...
Pondering
given this situation:
(let [m {:x1}
{itself :x} m
{from-list :x} (list m)
{from-list-of-2 :x} (list m m)
{from-vector :x} (vector m)]
[(= itself from-list)
(nil? from-list-of-2)
(not= from-list from-vector)])
i'd say (= itself from-list) is surprising.
(nil? from-list-of-2) looks like magic.
(not= from-list from-vector) can easily result in hard to understand errors,
since all u need is to realize some lazy sequence, by spilling it into a vector with vec or (into [] ,,,), instead of doall
and BAMM your program might blow up at a distance.
in my specific situation the behaviour was even more magical, because i had function with an optional argument after the map destructuring one: (fn [arg1 {:as arg2 k :some/k} & [arg3]] ,,,), yet it behaved like (fn [arg1 & {:as arg2 k :some/k}] ,,,), which is apparently the same as (fn [arg1 {:as arg2 k :some/k}] ,,,), when called with a map or a list of a single map.
Alex Miller (Clojure team)
that associative destructuring of a sequential thing does anything outside the case of & args is not anything that has ever been documented. what do you expect it to even mean to do associative destructuring on a sequential collection of one element?
Activity
NoahTheDuke commentedon May 17, 2024
here are some relevant threads on clojurian slack highlighting the confusion:
https://clojurians.slack.com/archives/C03S1KBA2/p1668533720466509
https://clojurians.slack.com/archives/C03S1KBA2/p1707982404402549
https://clojurians.slack.com/archives/C03S1KBA2/p1714580544135239
https://clojurians.slack.com/archives/C03S1KBA2/p1715935559111129
onetom commentedon May 17, 2024
I just tripped over this issue today too.
i thought it's a bug, but @puredanger said it's intended behaviour:
https://ask.clojure.org/index.php/12374/map-destructuring-works-on-singleton-lists?show=12380#c12380
so the
Destructuring in Clojure
guide should definitely mention these peculiarities!it provides this example:
so it should mention how it behaves the same way, as a function defined as:
when called as
(configure 12 (list :debug true))
or(configure 12 (list {:debug true}))
,but not equivalent, when called as
(configure 12 (vector :debug true))
or(configure 12 (vector {:debug true}))
.im not sure what reasoning can be given for this behaviour though...
Pondering
given this situation:
i'd say
(= itself from-list)
is surprising.(nil? from-list-of-2)
looks like magic.(not= from-list from-vector)
can easily result in hard to understand errors,since all u need is to realize some lazy sequence, by spilling it into a vector with
vec
or(into [] ,,,)
, instead ofdoall
and BAMM your program might blow up at a distance.
in my specific situation the behaviour was even more magical, because i had function with an optional argument after the map destructuring one:
(fn [arg1 {:as arg2 k :some/k} & [arg3]] ,,,)
, yet it behaved like(fn [arg1 & {:as arg2 k :some/k}] ,,,)
, which is apparently the same as(fn [arg1 {:as arg2 k :some/k}] ,,,)
, when called with a map or a list of a single map.filipesilva commentedon Jul 1, 2025
Some extra details from me hitting it and getting some very helpful replies in Clojurians:
It is not limited to trailing maps:
It is undefined behaviour:
It is tested for in core:
https://github.com/clojure/clojure/blob/da1c748123c80fa3e82e24fc8e24a950a3ebccd9/test/clojure/test_clojure/data_structures.clj#L1334-L1338