6
votes

I'm just starting to learn clojure and have been reading some simple examples and then doing my best to rtfm for concepts.

However I'm a bit confused by what val is doing in the example below. This has been taken from the Clojure doc examples for val.

(first {:one :two}) ;; => [:one :two]

Here, a hash-map with a key of :one and a value of :two is being passed to first. Behind the scenes, Clojure converts this hash-map to a sequence of vectors. Since there is only one vector in this sequence, it returns [:one :two].

(val (first {:one :two})) ;; => :two
(val [:one :two]) ;; => ClassCastException clojure.lang.PersistentVector cannot be cast to java.util.Map$Entry
(val {:one :two}) ;; => ClassCastException clojure.lang.PersistentArrayMap cannot be cast to java.util.Map$Entry

If I try to call val on a (I think) a hash-map (I realize it's actually a "persistent array map"), I get the exception as seen above.

I'm also confused by the following:

(first {:one :two}) ;; # => [:one :two]  (this is a vector right?)
(val [:one :two]) ;; # => ClassCastException (why doesn't this give back the same result as the example above?)

Why can't I just plug the result of (first {:one :two}) into val and get the same result?


Additionally, another example listed on the page is the following:

(map val {:a 1 :b 2}) ;; => (1 2)

Here's how I read the line. Take the array-map {:a 1 :b 2}. For each key-value pair, call val on the pair to return the value. Return a sequence from the resulting calls to map. Is this the correct way to read the problem?

As always, thanks for any and all help.

3
A map entry is a vector, but not all vectors are map entries. val only works on map entries.noisesmith
... to wit, (type (first {:one :two})) => clojure.lang.MapEntryThumbnail

3 Answers

7
votes

a sequence of a map produces MapEntry values as you've noted, which look like and can be compared with vectors

user=> (= (first {:a 1 :b 2}) [:a 1])
true

but aren't the same class

user=> (= (class (first {:a 1 :b 2})) (class [:a 1]))
false

So although the output on the repl of (first {:a 1}) looks like a vector, it isn't, it's a MapEntry, so it can be passed to val, but the vector [:a 1] cannot, hence the class cast exception.

Your reading of what map is doing is correct at a high level, a little more specific might be "For each entry in the sequence from {:a 1 :b 2} (which are MapEntry values) call the function val on each item (a MapEntry), and generate a sequence from the results".

This will explain why something like

user=> (map val '([:a 1] [:b 2]))

will cause the same ClassCastExceptions as the sequence generates Vector elements, not MapEntry elements.

6
votes

val returns value of a map entry, not a map.

(first {:one :two}) return the first map entry (although it appears to be just a vec)

(map val {:one :two}) return the value of every entry, and is equivalent to (vals {:one :two})

3
votes
(first {:one :two}) ;; # => [:one :two]  (this is a vector right? No, it's not.)

[:one :two] in this case is a MapEntry, not a vector.