top | item 39184351

(no title)

jordibc | 2 years ago

It does preclude it, but clojure found an arguably elegant solution to it, using recur[1] instead. As a plus, in addition to achieving the same result as tail-call elimination, it does check that the call is indeed in tail position, and also works together with loop[2].

For me, it made me not miss tail-call elimination at all.

[1] https://clojuredocs.org/clojure.core/recur

[2] https://clojuredocs.org/clojure.core/loop

discuss

order

lispm|2 years ago

In Scheme:

    (define (foo)
      (bar))
the call to bar is a tail call. How does recur optimize this? Well, it doesn't, since "general TCO" / "full TCO" means that any tail call gets optimized, such that the stack does not grow. Clojure recur/loop is just a loop notation.

Looping construct in most languages provides a simple conversion of self recursion (a function calls itself recursively) to a loop: update the loop variables and then do the next loop iteration.

But the general case of tail calls is not covered by a simple local loop, like what is provided by Clojure.

whateveracct|2 years ago

The issue arises when you program really heavily with closures and function composition. You sadly cannot do functional programming as in "programming with functions" without care on the JVM.

packetlost|2 years ago

It is, IMO, a missed opportunity to use a hard-coded identifier for `recur`ing instead of the `(let sym ((...)) ...)` form that would let you nest loops.

Aside from that, I agree. Tail-call optimization's benefits are wildly overblown.

whateveracct|2 years ago

The benefits aren't overblown if you are someone who learned Lisp with a functional approach. As in, using higher-order functions etc. You have to be careful whenever you approach a problem that way on the JVM.