scape
scape

Reputation: 707

stubborn loop in clojure, let not getting evaluated

I have the following code:

(do
  (prn "sleeping for 60 seconds")
  (Thread/sleep 6000)
  (prn "kicking off calendar downloads @ " (new java.util.Date))
  (let [links (map #(clojure.string/split % #",") (clojure.string/split (clojure.string/replace (slurp "calendars.csv") #"\r" "") #"\n"))]
   (map download links))

I noticed let evaluation must be last or else it does not get evaluated. This is confusing to me. Nonetheless when I implement this in a loop, let is never evaluated since I think recur ends up being inferred.

(while
(do
  (prn "sleeping for 60 seconds")
  (Thread/sleep 60000)
  (prn "kicking off calendar downloads @ " (new java.util.Date))
  (let [links (map #(clojure.string/split % #",") (clojure.string/split (clojure.string/replace (slurp "calendars.csv") #"\r" "") #"\n"))]
   (map download links))
  )) 

I'd prefer for sleep to be at the end of this do section as well, but that is moot really.

How can I get let to evaluate? Does it have to do with my use of map? What am I misinterpreting here?

Download is a function:

(defn download [[calname ics]]
  (prn "attempting cal download: " calname)
  (try (spit (str dest calname ".csv") (slurp ics))
  (catch Exception e (str "caught exception: " (.getMessage e)))))

Upvotes: 2

Views: 240

Answers (2)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91554

you have been bitten by the lazy bug:

  • I noticed let evaluation must be last or else it does not get evaluated.
    this is because the result of calling map is lazy so it is only evaluated if something reads the result. If you put it last, then it becomes the return value of the do which gets printed by the repl, which causes it to actually run.

  • when I implement this in a loop, let is never evaluated:
    while will keep running until the return value of the expression is false. In this case the return value of the expression is a lazy sequence that would cause some pages to be downloaded if anyone ever looked at it. In this case nothing looks at the result so nothing is ever downloaded, and since it's not false it just keeps running.

  • How can I get let to evaluate?:
    wrap the calls to map in (doall (map ... ....)) to cause the lazy sequence to be evaluated.

Upvotes: 6

paul
paul

Reputation: 1705

My guess at what is happening:

map is a lazy function, so when it's the last item in the do, it is getting evaluated since that's what is being returned by the do. If it's not the last thing in the do, then its return value is not being used and so it's not getting evaluated.

I ran into something similar myself.

Try using doseq instead of map.

Upvotes: 1

Related Questions