Reputation: 1491
I was practicing one Clojure tutorial and had to ensure that a for loop was executed so I put a println
command there, but it did not display messages.
So now I have got the question...
This code prints Tom's name:
(ns tutorial.core)
(defn -main []
(println 'Jane)
(for [a ['Tom]]
(println a))
;; 'Kate
)
tutorial.core> (-main)
Jane
Tom
(nil)
tutorial.core>
but this not:
(ns tutorial.core)
(defn -main []
(println 'Jane)
(for [a ['Tom]]
(println a))
'Kate
)
tutorial.core> (-main)
Jane
Kate
tutorial.core>
Why? In which cases can we expect that println
will not print texts?
Upvotes: 0
Views: 285
Reputation: 29958
As Lee says, if you only want side effects like printing, a doseq
is the best solution as it never returns a value other than nil
.
If you do want to use a for
loop, you can remove the laziness by wrapping it inside a (vec ...)
expression, which will force the for
loop to run immediately. Thus we get:
(println :start)
(vec
(for [a [1 2 3]]
(println a)))
(println :end)
with result:
:start
1
2
3
:end
Without the vec
, we get the behavior you saw:
(println :start)
(for [a [1 2 3]]
(println a))
(println :end)
with result:
:start
:end
I almost never want a lazy result, as the uncertainty over when a computation occurs can make debugging difficult. I use the above construct so often that I wrote a small macro forv
that always returns a vector result, similar to the mapv
function.
Upvotes: 1
Reputation: 144136
for
is not a loop, it is a sequence comprehension which returns a lazy sequence. Your for
expression will therefore only execute its side-effects (calls to println
) when the returned sequence is evaluated. The REPL evaluates the values returned from your calls to -main
so it can print them.
Your first example returns a lazy sequence which is evaluted by the REPL causing the (println 'Tom)
call to be evaluated. Since println
returns nil
, the resulting sequence contains a single nil
value - this is the (nil)
you see in the output.
Your second example creates the same sequence but does not evaluate it, instead 'Kate
is returned from the function and the REPL prints that.
If you want an imperative for loop you should use doseq
:
(defn -main []
(println 'Jane)
(doseq [a ['Tom]]
(println a))
'Kate)
Upvotes: 1