7

Clojure's for macro accepts two arguments: a sequence of binding forms, and a body expression. As such, if I want to do multiple things in the loop, I have to wrap multiple expressions in a do block to make them a single expression.

Compare:

(doseq [a (range 3)]
  (prn 'a a)
  (inc a))

to:

(for [a (range 3)]
  (prn 'a a)
  (inc a))

The doseq works as expected. The for complains:

clojure.lang.ArityException: Wrong number of args (3) passed to: core/for

My question is, why doesn't this work? Why didn't the designer(s) of Clojure allow multiple "body" expressions in the for loop, like they did in doseq and when? It's not as if there is any semantic ambiguity, right?

Jeff Terrell Ph.D.
  • 2,563
  • 26
  • 39
  • 1
    One big difference is that `for` is lazy, while `doseq` (being primarily for inducing side-effects) is not. `when` evaluates its body in an implicit `do`. See also http://stackoverflow.com/q/4725417 – Robert Harvey Jul 21 '14 at 15:26

1 Answers1

14

Clojure's for is not a loop, it is lazy list comprehension. Since only the value of last expression in the body of a (implicit) do is returned, any other expressions would have to exist merely for side effects. Execution for side-effects is the job of doseq, which is why it has an implicit do. That for does not should be a reminder, do you really want side effects in your lazy sequence? Though there are legitimate reasons for doing so, care is warranted that those side effects execute when you want.

(def foo (for [a (range 32)]
           (do
             (prn (str "a is " a))
             (inc a))))

(take 1 foo)
;=>
  ("a is 0"
   "a is 1"
   ...
  "a is 31"
  1) ; <= the return value

(take 10 foo)
;=>
  (1 2 3 4 5 6 7 8 9 10) ; aw, no side effects this time
A. Webb
  • 26,227
  • 1
  • 63
  • 95
  • A good answer, although I think that the chunking part at the end is mostly a red herring, and not explained well enough to be useful anyway. – amalloy Jul 21 '14 at 20:06