1
votes

I'm learning about Clojure using the book Seven Weeks Seven Languages and I'm feeling like I'm missing the point and/or not getting it.

Question:

Write a function called (collection-type col) that returns :list, :map, or :vector based on the type of collection col.

My Solution:

(defn collection-type
    "returns collection type for list map or vector"
    [col]
    (def look-up {:list "class clojure.lang.PersistentList", :map "class clojure.lang.PersistentArrayMap", :vector "class clojure.lang.PersistentVector"})
    (if (= (look-up :list) (str (type col))) (println :list))
    (if (= (look-up :map) (str (type col))) (println :map))
    (if (= (look-up :vector) (str (type col))) (println :vector)))

It works fine but I feel like I'm missing the point, does anyone have any insight/advice/guidance here? It just seems so ugly and in-elegant.

2
def can only create globals. As such, it is usually out of place inside a function.noisesmith

2 Answers

3
votes

Other answers depend on testing the concrete type of the collection which is fraught with danger.

Maps for example have a different concrete implementation for different sizes as a performance optimization.

Consider:

(type {})
;=> clojure.lang.PersistentArrayMap

(type (zipmap (range 100) (range 100)))
;=> clojure.lang.PersistentHashMap

Since clojure already has predicates to test for the necessary collections why not use those and make the solution more robust

(defn collection-type [coll] (condp #(%1 %2) coll
                                    map?    :map
                                    vector? :vector
                                    list?   :list))
3
votes

Edit

Although the following answers the question, it does not mend the fault in the offered code noted by @sw1nn.


How can we simplify this?

  • Compute with the types, not their string representations.
  • Invert the look-up map to give the keyword-tag of a given type.
  • Return the value. Leave the caller to print it.

Using the look-up map as a function (as you already do), we get

(defn collection-type
  "returns collection type for list map or vector"
  [coll]
  (let [look-up {clojure.lang.PersistentList     :list
                 clojure.lang.PersistentArrayMap :map
                 clojure.lang.PersistentVector   :vector}]
    (look-up (type coll))))

Then

(collection-type [])
;:vector

(collection-type (list 1))
;:list

But

(collection-type ())
;nil

since

(type ())
;clojure.lang.PersistentList$EmptyList