What is the difference among the functions doall dorun doseq and for?
ClojureClojure Problem Overview
What is the difference between the functions doall
, dorun
, doseq
, and for
?
I found some information scattered throughout the internet, but I think it would be better to centralize that information here.
Clojure Solutions
Solution 1 - Clojure
dorun
, doall
, and doseq
are all for forcing lazy sequences, presumably to get side effects.
dorun
- don't hold whole seq in memory while forcing, returnnil
doall
- hold whole seq in memory while forcing (i.e. all of it) and return the seqdoseq
- same asdorun
, but gives you chance to do something with each element as it's forced; returnsnil
for
is different in that it's a list comprehension, and isn't related to forcing effects. doseq
and for
have the same binding syntax, which may be a source of confusion, but doseq
always returns nil
, and for
returns a lazy seq.
Solution 2 - Clojure
You can see how dorun
and doall
relate to one another by looking at the (simplified) source code:
(defn dorun [coll]
(when (seq coll) (recur (next coll))))
(defn doall [coll] (dorun coll) coll)
dorun
runs through the sequence, forgetting it as it goes, ultimately returningnil
.doall
returns its sequence argument, now realised by thedorun
.
Similarly, we could implement doseq
in terms of dorun
and for
:
(defmacro doseq [seq-exprs & body]
`(dorun (for ~seq-exprs ~@body)))
For some reason, performance perhaps, this is not done. The standard doseq
is written out in full, imitating for
.