putting 2-element colls into a map: works with vectors, but not with lists?

610 views
Skip to first unread message

John Gabriele

unread,
Jun 24, 2013, 11:49:44 AM6/24/13
to clo...@googlegroups.com
Why does `into` fail when the 2-element collections are lists and not vectors? :

~~~
user=> (into {} [[:a 1] [:b 2]])
{:a 1, :b 2}
user=> (into {} ['(:a 1) '(:b 2)])

ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry  clojure.lang.ATransientMap.conj (ATransientMap.java:44)
~~~

or even:

~~~
user=> (conj {} [:a 1])
{:a 1}
user=> (conj {} '(:a 1))

ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry  clojure.lang.APersistentMap.cons (APersistentMap.java:42)
~~~

Sean Corfield

unread,
Jun 24, 2013, 12:14:56 PM6/24/13
to clo...@googlegroups.com
On Mon, Jun 24, 2013 at 8:49 AM, John Gabriele <jmg...@gmail.com> wrote:
> Why does `into` fail when the 2-element collections are lists and not
> vectors? :

Because the implementation special cases vectors :)

It's the one place where a two element vector is treated like a
Map$Entry so that you are not forced to somehow create Map$Entry
instances for the key/value pairs.

There are some other quirks around that special casing - for example:

user=> (conj {} {:a 1 :b 2})
{:b 2, :a 1}
user=> (conj {} '([:a 1] [:b 2]))
ClassCastException clojure.lang.PersistentVector cannot be cast to
java.util.Map$Entry clojure.lang.APersistentMap.cons
(APersistentMap.java:42)

So conj on a map acts like a map-preserving concat since the second
argument can be a map, not just a new key/value pair to add, yet you
cannot conj a sequence of two-element vectors onto a map.

You can see here that maps contain MapEntry elements, not actual two
element vectors:

user=> (first {:a 1})
[:a 1]
user=> (type (first {:a 1}))
clojure.lang.MapEntry
user=> (type [:a 1])
clojure.lang.PersistentVector

Things can get even stranger if you start doing interop between
Clojure and other languages that have slightly different map
implementations...
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

John Gabriele

unread,
Jun 24, 2013, 2:39:38 PM6/24/13
to clo...@googlegroups.com
On Monday, June 24, 2013 12:14:56 PM UTC-4, Sean Corfield wrote:
On Mon, Jun 24, 2013 at 8:49 AM, John Gabriele <jmg...@gmail.com> wrote:
> Why does `into` fail when the 2-element collections are lists and not
> vectors? :

Because the implementation special cases vectors :)

It's the one place where a two element vector is treated like a
Map$Entry so that you are not forced to somehow create Map$Entry
instances for the key/value pairs.

There are some other quirks around that special casing - for example: {snip}

Thanks, Sean!
 

Michael Gardner

unread,
Jun 24, 2013, 2:57:46 PM6/24/13
to clo...@googlegroups.com
On Jun 24, 2013, at 11:14 , Sean Corfield <seanco...@gmail.com> wrote:

> On Mon, Jun 24, 2013 at 8:49 AM, John Gabriele <jmg...@gmail.com> wrote:
>> Why does `into` fail when the 2-element collections are lists and not
>> vectors? :
>
> Because the implementation special cases vectors :)
>
> It's the one place where a two element vector is treated like a
> Map$Entry so that you are not forced to somehow create Map$Entry
> instances for the key/value pairs.

Huh. I had assumed this would work with any 2-element collection, like destructuring. Why not? Performance reasons?

Michael-Keith Bernard (SegFaultAX)

unread,
Jun 25, 2013, 4:03:59 PM6/25/13
to clo...@googlegroups.com
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L24

The implementation assumes you're attempting to conj one of the following 3 things into the hash map:
  1. A MapEntry object
  2. A vector of the format [key value]
  3. A seq of MapEntry objects (commonly created via http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html#entrySet() and the like)

Michael Gardner

unread,
Jun 25, 2013, 6:45:58 PM6/25/13
to clo...@googlegroups.com
On Jun 25, 2013, at 15:03 , Michael-Keith Bernard (SegFaultAX) <mkbern...@gmail.com> wrote:

> https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L24
>
> The implementation assumes you're attempting to conj one of the following 3 things into the hash map:
> • A MapEntry object
> • A vector of the format [key value]
> • A seq of MapEntry objects (commonly created via http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html#entrySet() and the like)

If I read you correctly, you're saying the reason 'into et al. can't accept arbitrary two-element collections for adding to a map is that APersistentMap's .cons method does something special with seqable things that aren't vectors. Right?

Any insight into why Rich would have written .cons that way, instead of putting that seq-of-MapEntry functionality into a separate method?
Reply all
Reply to author
Forward
0 new messages