Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Clojure's for macro only accept one body expression?

Tags:

clojure

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?

like image 352
Jeff Terrell Ph.D. Avatar asked Jul 21 '14 15:07

Jeff Terrell Ph.D.


1 Answers

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
like image 135
A. Webb Avatar answered Oct 13 '22 00:10

A. Webb