Reputation: 574
Can someone please explain why Clojure's while
macro doesn't return a value?
The documentation says "Presumes
some side-effect will cause test to become false/nil." Okay, so we use swap!
to modify the atom which is used in the test case. But can't we still have a return value (perhaps returning repeatedly, as in the case with loop
), along with the side effects?
Looking at the source, it seems like the cause could be something to do with how recursion and macros work, but I don't know enough about the internals of that to speculate.
Example:
The following code only returns nil
:
(let [a (atom 0)]
(while (< @a 10)
(+ 1 @a)
(swap! a inc)))
That is, it does not return the value of (+ 1 @a)
, nor does it return any other value or function that is inside the while
loop. If we want to get some value calculated via the while
loop, we could use print
, but then we can't easily use the value in some other operation. For a return value, we have to use a second atom, like this:
(let [a (atom 0)
end (atom 0)]
(while (< @a 10)
(swap! end #(+ 1 %))
(swap! a inc))
@end)
Upvotes: 4
Views: 1068
Reputation: 3205
Can someone please explain why Clojure's while macro doesn't return a value?
But can't we still ... along with the side effects?
Side effects are there for when you need them, but if you are too free in mixing them throughout your regular code then you are missing out on one of the huge benefits of functional programming.
In clojure.core, the 'functions' that are used for the purpose of realising some side effect are always demarcated as such and it's a good idea to follow this in your own code too.
Upvotes: 1
Reputation: 1582
Using (source while)
at the REPL, we can see it is implemented like this:
(defmacro while
"Repeatedly executes body while test expression is true. Presumes
some side-effect will cause test to become false/nil. Returns nil"
{:added "1.0"}
[test & body]
`(loop []
(when ~test
~@body
(recur))))
So, it executes the body, then checks the condition, rinse, repeat. By the time the condition is false, the most recent return value from the body is gone.
But what exactly are you trying to do? Do you really need an atom for this?
This code counts up to 10 and returns 10:
(loop [a 0]
(if (< a 10)
(recur (inc a))
a))
Upvotes: 3