Elements of Clojure Questions

This is a place to ask questions about the book “Elements of Clojure”. Anyone can use it for this purpose but be aware there is a book club that is doing live discussions and its main purpose is to post questions before and after the live meetings.

4 Likes

I have a question from the 4th section of the 2nd chapter, No one should have to know you’ve used binding.

I’m trying to recreate the lazy/eager evaluation I think the chapter is applying.

All my code is the same as the chapter except for the library/compute shim in the c function. I changed it to:

(def ^:dynamic *turbo-mode?* true)
(defn c [x] (print x *turbo-mode?* ", "))
(defn b [x] (c x))
(defn a [x] (b x))

As promised, after 32 values, you get a few surprises in your evaluation of the chunked-seq:

(def values [1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5])
(slowly (let [values' (map a values)] 
    (if (empty? values') 
        (throw (IllegalArgumentException. "empty input"))
         values')))


1 false , 2 false , 3 false , 4 false , 5 false , 1 false , 2 false , 3 false , 4 false , 5 false , 1 false , 2 false , 3 false , 4 false , 5 false , 1 false , 2 false , 3 false , 4 false , 5 false , 1 false , 2 false , 3 false , 4 false , 5 false , 1 false , 2 false , 3 false , 4 false , 5 false , 1 false , 2 false , (nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil 3 true , 4 true , 5 true , nil nil nil nil)

Then I change the a function to the recommended improvement:

(defn a
    ([x]
        (b x))
    ([x turbo-mode?]
        (binding [*turbo-mode?* turbo-mode?]
            (b x))))

I run the slowly macro again and get the same result as above.

I think I understand how this is safer. The a function is referentially transparent unless we turn on turbo-mode. Is that all we’re gaining out of this? We’re still going to have to live with the fact that we think we’re computing everything slowly even though the chuncked-seq is going to evaluate outside of the binding when it reaches a certain number of elements. In the case above, 32/35 elements are “evaluated slowly.” This is not explicit in the code at all and would need to be commented.

Or perhaps I’m missing something.

The idea is that adding a turbo-mode? param to a means you don’t need a slowly macro anymore. In the failing example, you can just do this:

(let [values' (map #(a % false) values)] 
    (if (empty? values') 
        (throw (IllegalArgumentException. "empty input"))
         values'))

And you will always get false, no matter where you realize the lazy sequence.

Ah ha! Thank you! Now I understand 100%.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.