Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Should I Iterate a Sequence?

Tags:

clojure

Below is my attempt to iterate a sequence of maps; the code fails due to the casting error: Exception in thread "main" java.lang.RuntimeException: java.lang.ClassCastException: clojure.lang.Cons cannot be cast to java.util.Map$Entry.

Can anyone explain/demonstrate how I should iterate the result-set? Thanks.

(with-connection db
                 (with-query-results rs ["select category from users group by category"]
                                     (doall
                                       (for [s [rs]] 
                                       (do (println (val s)))))))
like image 386
dj3 Avatar asked Aug 10 '26 18:08

dj3


1 Answers

You wrapped the rs into a vector. So s will be bound to the whole sequence, not the individual map entries. So when you call val it doesn't know what to do with a sequence. Hence the exception. This should work:

(with-connection db
  (with-query-results rs ["select category from users group by category"]
    (doall
      (for [rec rs
            s   rec] 
        (do
          (println (val s)))))))

However the ugly doall and do around the for should ring a bell, that something could be improved. And indeed for is used to construct another lazy sequence. This does not work well with side-effects as you intend in your example. You should use doseq in this case.

(with-connection db
  (with-query-results rs ["select category from users group by category"]
    (doseq [rec rs
            s   rec]
      (println (val s)))))

The interface for the bindings of doseq is identical to that of for. However it executes things immediatelly, and thusly realises any side-effects immediatelly. If you put multiple expressions in the body of a for, you have to wrap it into a do. This is a reminder that the body should produce a value. Multiple expressions however indicate side-effects. doseq therefore wraps the body into a do for you. So you can easily have multiple expressions. For illustration:

(doall
  (for [s seq-of-maps]
    (do
      (println (key s))
      (println (val s)))))

(doseq [s seq-of-maps]
  (println (key s))
  (println (val s)))))

As a rule of thumb: you need side-effects? Look for things starting in do!

As a rule of thumb 2: if something looks ugly (see above comparison), this should ring a bell.

like image 124
kotarak Avatar answered Aug 13 '26 15:08

kotarak