4
votes

I'm twisting my old java/python head the clojure way. Please help me to understand the lazy feature of clojure.

=> (def myvar (lazy-seq [1 2 (prn "abc")]))
#'user/myvar

The above is easy to understand. Since it's a lazy sequence, the (prn "abc") will not be evaluated, hence nothing printed.

=> (def myvar (lazy-seq [1 2 (prn undefined-var)]))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: undefined-var in this context, compiling:(NO_SOURCE_PATH:1) 

The above will raise an error as you can see. Why ?

My (wrong)understanding is, since it's lazy, the (prn undefined-var) could be legally here even the "undefined-var" has not been defined yet.

Please anyone point my understanding to the correct way.

3

3 Answers

9
votes

When the clojure reader finds

 (def myvar (lazy-seq [1 2 (prn undefined-var)]))

It needs to compile it, that is the reason why it throws the error, as the undefined-var is not defined. In the first case, it compiles ok, but it is not executed until you consume the seq.

8
votes

Both above answers provide you with good information on the subject, but I'll try to pin down the key problem here. When you write an s-expression on the REPL like (+ x 2) two things happen:

  1. parsing by the reader of the characteres which yields forms, such as symbols,
  2. evaluation of the forms.

Lazy evaluation pospones the second step, but in the first step, when reader encounters undefined-var it tries to convert it to a symbol, and finds that such symbol was not defined.

1
votes

You seem to have the correct understanding, it just thats not how the lazy-seq function works here is a typical example:

user> (lazy-seq (cons 4 (range 5)))
(4 0 1 2 3 4)

lazy-seq almost always takes a cons expression where the first argument is the first item in the sequence and the second argument is the code to produce the rest of the list. People verry rarely have to use lazy-seq directly in day to day Clojure, using forms like map reduce, filter, etc. are much more common.

the code in the function for producing the rest of the sequence also neeeds to be able to be compiled, in your case you can delay compilation as well by using eval though I would recommend not using eval for things like this in code others need to read; makes for great learning though ;-)

user> (def myvar (lazy-seq (cons 1 (eval '(prn undefined-var))))) ; don't do this at work
#'user/myvar
user> myvar
; Evaluation aborted.
user>