2

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)))))
2
  • possible duplicate of How to convert lazy sequence to non-lazy in Clojure Commented Jun 25, 2013 at 20:57
  • 3
    this is not a duplicate, that only answers part of this question. (though it's tempting to make a tag for 'clojure-lazy-bug' Commented Jun 25, 2013 at 21:25

2 Answers 2

6

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Curious, would this function solve this dilemma, like a shorthand version for doall, or is it too simplistic and shortsighted for all scenarios? (defn dmap [f & [m]] (doall (map f m)))
yes that's the idea, though it needs an apply before the map to have the same function as map
on second thought I'm going to look at doall's source and sort of replicate it I think
(def dmap (comp doall map))
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.