104

I tried the following in Clojure, expecting to have the class of a non-lazy sequence returned:

(.getClass (doall (take 3 (repeatedly rand))))

However, this still returns clojure.lang.LazySeq. My guess is that doall does evaluate the entire sequence, but returns the original sequence as it's still useful for memoization.

So what is the idiomatic means of creating a non-lazy sequence from a lazy one?

2
  • I'm surprised nobody has asked why you are concerned about the actual type of the return value of doall Commented Nov 1, 2017 at 17:14
  • 1
    You could convert to a vector: (vec (take 3 (repeatedly rand))) Commented Jan 23, 2018 at 12:33

5 Answers 5

177

doall is all you need. Just because the seq has type LazySeq doesn't mean it has pending evaluation. Lazy seqs cache their results, so all you need to do is walk the lazy seq once (as doall does) in order to force it all, and thus render it non-lazy. seq does not force the entire collection to be evaluated.

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

4 Comments

I've changed this to the accepted answer. On a related note, by what means can you determine if a LazySeq has previously been evaluated?
I believe you just call realized?.
There should probably be a realize operation to match realized?.
This is all very nice. But since some functions like contains? don't care whether you realized the lazy seq or not, this answers to the specific question as asked, but less so to the title of the question.
87

This is to some degree a question of taxonomy. a lazy sequence is just one type of sequence as is a list, vector or map. So the answer is of course "it depends on what type of non lazy sequence you want to get:
Take your pick from:

  • an ex-lazy (fully evaluated) lazy sequence (doall ... )
  • a list for sequential access (apply list (my-lazy-seq)) OR (into () ...)
  • a vector for later random access (vec (my-lazy-seq))
  • a map or a set if you have some special purpose.

You can have whatever type of sequence most suites your needs.

5 Comments

This is the best answer.
The accepted answer is technically correct, but this answer was most useful to me. I was trying to map a function over a vector and then spit the results to a file, and even after calling doall, the file contained "clojure.lang.LazySeq@address" instead of the contents of the sequence. Calling vec on value map returned got me what I needed to spit out to the file.
@JesseRosalia It's good to know that the one and only Rich Hickey response in all of SO was was technically correct. ;-)
(vec (my-lazy-seq)) is not so nice in situations like the following: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]] Since cheshire chooses to produce a lazy-seq from (json/parse-string)
Mitigation for the above was to use eager (json/parse-string-strict)
25

This Rich guy seems to know his clojure and is absolutely right.
Buth I think this code-snippet, using your example, might be a useful complement to this question :

=> (realized? (take 3 (repeatedly rand))) 
false
=> (realized? (doall (take 3 (repeatedly rand)))) 
true

Indeed type has not changed but realization has

6 Comments

It's worth noting, though, that you don't need to force the entire sequence for realized? to return true. E.g. (let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
This Rich guy :D haha
@nimrod :) the pun however was meant to be in "hís clojure".
For those who don't know, "the Rich guy" invented Clojure.
@AlexCoventry your example returns [true true]
|
8

I stumbled on this this blog post about doall not being recursive. For that I found the first comment in the post did the trick. Something along the lines of:

(use 'clojure.walk)
(postwalk identity nested-lazy-thing)

I found this useful in a unit test where I wanted to force evaluation of some nested applications of map to force an error condition.

Comments

5
(.getClass (into '() (take 3 (repeatedly rand))))

2 Comments

This is a terrible idea. It reverses the input seq.
Of course, in this case reversing the input makes no difference, since they are just 3 random numbers.... :-)

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.