|
Sure, and Clojure has '&' in function argument lists for rest arguments. Here's a Clojure example: user=> (for [x [0 1 2 3 4 5]
:let [y (* x 3)]
:when (even? y)
z [1 2]
[y z])
([0 1] [0 2] [6 1] [6 2] [12 1] [12 2]) The binding vector for 'for' takes alternating name/value pairs, except it also allows these special keywords whose semantics affect the environment of the subsequent bindings, and whether the bindings are even evaluated, as well as the environment of the body expression (and whether it's evaluated). This strikes me as kind of ugly. The same can be done like this, using that mdo macro: > (monads/with-monad monads/sequence-m
(mdo x <- [0 1 2 3 4 5]
(let [y (* x 3)]
(when (even? y)
(mdo z <- [1 2]
(monads/m-result y))))))
with identical results, or, as above, like this: > (monads/with-monad monads/sequence-m
(mdo x <- [0 1 2 3 4 5]
let y = (* x 3)
(when (even? y)
(mdo z <- [1 2]
(monads/m-result [y z])))))
Of course, if you think of a macro like "for" of consisting of a binding vector followed by an expression (or a sequence wrapped in an explicit do), there's no other place for the whens and lets that you want to use to control evaluation of (the environments of) the various bindings than the binding vector itself.If I'm not mistaken, Common Lisp's loop macro (e.g.) isn't like this---that is, it establishes its own little mini language, instead of mimicking forms found elsewhere in the language (the way Clojure's for loop control constructs are all stuffed into a "binding vector" similar to what might be found in a defn). Now it's true that something like "let y = (* x 3)" is not very Lispy at all. But neither, I think, is having something like ":when (even? y)", a sequence of two elements in a flat vector, actually govern bindings and execution of things that follow it but which it doesn't enclose. |
For the same reasons (concision, idiom, and clarity), forgive me but I don't like the monads/with-monad versions you've given. "monads/with-monad monads/sequence-m" is much too verbose — I want an operation this simple to be lightweight. It doesn't feel idiomatic to me for looping and pairing things, though that may just be my ignorance. And it feels like some powerful and general infrastructure is being invoked to do something whose triviality I would rather see the code bring out. Looping is the canonical example, I think, of something that one does so often and has such repetitive-yet-assorted patterns that a little language is perfect for the job. But I'm biased, as I said.
By the way, I've been puzzling over that FOR form trying to make out how it ends up producing a Cartesian product out of '(0 1 2 3 4 5) and '(1 2). In CL, if you make the bindings parallel like that, as in (loop for x in foo for y in bar...), it means "take an x from foo and a y from bar and keep going as long as they're both producing new values". So to get a Cartesian product, I had to make an explicit nested loop in my translation above. Both idioms are useful, but I use the parallel one quite a bit more than the nested one. Does FOR handle both?
p.s. You're missing a square bracket after [1 2]. I was puzzling even more before I realized that :)