0

I am a beginner in clojure and have a puzzled problem:

(do (println "ok") (for [x [1 2 3]] (println x)))

output: ok 1 2 3

I can understand this,but


(do (for [x [1 2 3]] (println x)) (println "ok"))

output: ok nil

Why the for function is not executed?

hq2999
  • 3
  • 1
  • This is a beginner-trap: `for` is a list comprehension and has basically nothing to do with the imperative for-loops from other languages. `for` is lazy - if no one consumes the result, no results are generated. – cfrick Apr 28 '22 at 09:38

3 Answers3

3

The (println x) forms in your examples are side-effects which will only occur if the for lazy sequence is realized.

Here is the first example reformatted:

(do
  (println "ok")
  (for [x [1 2 3]]
    (println x)))

do evaluates the expressions in turn and returns the value of the last one. Here the last expression is a lazy sequence. If you evaluated this in a REPL, the lazy sequence will be realized to print its elements (giving value (nil nil nil)) and cause the side-effects of printing 1 2 3.

Here is the second example reformatted:

(do
  (for [x [1 2 3]]
    (println x))
  (println "ok"))

Again do evaluates the expressions in turn. The first one is a lazy sequence but its value is not used and so is never realized. This means the side-effects of printing 1 2 3 don't occur.

Steffan Westcott
  • 2,121
  • 1
  • 3
  • 13
1

In short: the do macro evaluates all arguments and returns the value of the last one and this forced the elements of the lazy sequence to realize.

I assume you evaluated both expression in the REPL.

Because of the for macro, the first expression returns a lazy sequence. The REPL forces the evaluation of the returned value (so it can display the result of evaluation) so each element of the sequence is realized with the side effect of printing to the screen.

In the second case, the return value is nil because the last expression in the do form was a println call. The lazy sequence is also created in this case but its elements are never realized because the value is never used.

If you want to force evaluation of a sequence, you can use doall or dorun. If you are not interested in the sequence and only need a loop construct, you can use doseq instead of for.

erdos
  • 3,135
  • 2
  • 16
  • 27
0

The other answers already give good background. If you ever need the advanced features of for but don't want the lazy aspect, just wrap the whole thing in a (vec ...) form:

(vec (for [x [1 2 3]]
       (println x)))

This is already done for you in the forv macro that can be seen here.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • doseq is the right answer. doseq has all the bells and whistles of for, but doseq is intended to make side effects (so it isn't lazy and doesn't accumulate a needless answer). See manual at https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/doseq – Biped Phill Apr 29 '22 at 22:02