Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why `into` a map (`(into {} ,,,)`) works with `vectors` but not with `lists`, in Clojure?

Tags:

clojure

I can build a map with a list of vectors:

user=> (into {} (list (vector "a" "b") (vector "c" "d")))
{"a" "b", "c" "d"}

But if I try to do it with a list of lists, it fails:

user=> (into {} (list (list "a" "b") (list "c" "d")))
Execution error (ClassCastException) at user/eval3 (REPL:1).
class java.lang.String cannot be cast to class java.util.Map$Entry (java.lang.String and java.util.Map$Entry are in module java.base of loader 'bootstrap')

Why?

like image 986
jgomo3 Avatar asked Dec 31 '22 18:12

jgomo3


1 Answers

When you do (into {} coll) it is the equivalent of (reduce conj {} coll) so each element of your coll is used as the second argument to conj with a hash map as the first argument.

conj is built into clojure.lang.RT and it calls (in this case) APersistentMap.cons() to add the element which is here: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L24

As you can see from that, if the argument is a MapEntry, it'll get added to the hash map as a key/value pair: (seq my-hash-map) produces a sequence of MapEntry items.

If the argument is a vector that has two elements, it'll get added as a key/value pair where the key is the first element of that vector and the value is the second element of that vector.

Otherwise, it tries to convert the argument to a sequence and then casts each element to MapEntry and adds it as a key/value pair.

When you pass a list of vectors (two-element vectors), they are added as key/value pairs per the second case above. When you pass a list of lists, the third case is invoked and it tries to cast each element of your inner list to a MapEntry -- which fails because those elements are String's.

like image 157
Sean Corfield Avatar answered May 20 '23 20:05

Sean Corfield