Reputation: 23
I'm very newbie with Clojure, and for practice I am trying to apply a simple algorithm for semi random numbers.
A few days ago, I read about loops in the docs of clojure, and how they work using recur
, so I tried to write a loop with this lines of code:
(def numbers_semi_random
(fn []
(loop[Xn 4 count 0]
(while (and (not (= Xn m)) (< count m))
(println (mod (+ (* 5.0 Xn) 7.0) m))
(recur (mod (+ (* 5.0 Xn) 7.0) m) (inc count))
))))
But when I execute the code, this error is displayed
CompilerException java.lang.UnsupportedOperationException: Can only recur from tail position, compiling
What's happening? recur
is not in the tail of the function?
Upvotes: 2
Views: 1229
Reputation: 84331
It seems likely that your intention would be accomplished by using when
in place of while
(your loop would then keep printing updated values of Xn
until the condition was met), but to explain the exception:
From the perspective of the compiler, the problem is that while the while
form is indeed in tail position with respect to your loop
, the while
macro's expansion is such that nothing in the while
's body is in tail position with respect to the while
form. (Were the body in tail position w.r.t. the while
form, it would also be in tail position w.r.t. the enclosing loop
.)
The reason this is so is that while
expands to a loop
expression and supplies its own recur
after the user-provided body:
(while test
body...)
;; expands to
(loop []
(when test
body...
(recur)))
So, if you supply a recur
as part of the body of while
, what you get is
(loop []
(when test
...
(recur ...) ; from user code
(recur)))
Here the user-supplied (recur ...)
is not in tail position with respect to its immediately enclosing loop
, and this is what the compiler complains about.
Upvotes: 7
Reputation: 20194
The tail position is the last form that needs to be evaluated before leaving scope. When using loop
, the recur
statement must be able to start fresh with new bindings, no further calculations from the previous loop left to be done.
In your code, you have a while
block in the tail position of the loop
. The place where you try to call recur
is not in the loop's tail position (though it is in the tail of its tail).
Perhaps you intended for the recur
call to be outside the while
? But the comparison of loop bindings Xn
and count
to the global variable m
in the test for while
makes me think there is more to be untangled here. How many nested loops do you want here?
Upvotes: 3