4 ms·
Why Clojure? Clojure has cons, car, cdr which you can use in the classic Lisp sense. The only thing you CAN'T do is have a dotted pair or dotted list in the l
by DannyB2 7y ago
Why Clojure?
Clojure has cons, car, cdr which you can use in the classic Lisp sense. The only thing you CAN'T do is have a dotted pair or dotted list in the last cons cell.
One might argue that you cannot surgically alter data structures in Clojure. But you can! It's just that the function doing the alteration returns an entirely new data structure with the alteration in place -- but without the expected inefficiency of a copy operation. If you have an array of a billion elements, and alter one of the elements, you get back a new array with the alteration. But it is not a COPY of the entire array (with the expected time required to copy). The original array without the alteration also still exists -- but you don't have twice the memory usage now that there seem to be two slightly different arrays with these billion elements. Yet accessing or altering any element in the array has close to the performance you would expect of an actual array implementation.
- MadWombat 7y agoTail recursion? As far as I know, Clojure doesn't and cannot have it because JVM doesn't support it.
- holtalanm 7y agodon't they get around that by compiling the tailrec to bytecode as a while loop? other languages supporting tail recursion on the jvm do that. I don't know enough about clojure to say that is how they do it, though.
- lvh 7y agoYes, but you have to be explicit that it's a recur by using the recur special form. (I'm not convinced that matters, but some people get hung up on it.)
- holtalanm 7y agoyeah, there is special syntax for it in other jvm languages that support tail recursion, as well (kotlin). i honestly don't know how they would do it otherwise, really. I actually liked the `recur` syntax of clojure when i was playing around with it a few years ago, though.
- lvh 7y agoI don't see why the compiler couldn't be aware of the function it's compiling and special-cases that tail recursion; that's what every other compiler does.
- DannyB2 7y agoTail calls aren't just for loops. Tail calls can be across functions. A calls B calls C calls A calls B ... repeat until ... one of the functions solves the problem and does a return. Then the stack is unwound. But with TCO all of the tail calls simply re-use the current stack frame and JUMP to the next function.
- bitwize 7y agoKawa supports TCO with an optional flag. It uses trampolines to work around the JVM's limitations. This makes Kawa more akin to a real Scheme. (Still doesn't have full conts, only upward conts which are homeomorphic to Java exceptions.) Clojure could have done the same, but Rich Hickey deliberately chose not to, instead providing a special 'rec' construct for tail self calls. But this doesn't make Clojure not a Lisp, it only makes it not a Scheme.
- lvh 7y agoClojure has `recur` that does what you want. I'm not sure why it matters what the JVM supports. x86_64 doesn't do tail recursion either; it's the compiler's job to translate. Let's separate the `recur` vs the function name itself for a minute: if Clojure automatically took a function body with a tail-position recursion using the name of the function itself and translated it to a loop, would you agree that's tail recursion optimization?
- DannyB2 7y agoRecur is useful in Clojure, but still not a replacement for tail calls. Imagine two routines that call each other. A calls B, which calls A, which calls B, and continues recursion until the real answer is calculated, then returns and unwinds the stack. With real tail calls, there is no stack expansion. When A calls B, the original stack frame of A is overwritten to be the new stack call frame for B, and then A does a JUMP to the right code in B which eventually will do the RETURN instruction (or recursively call A).
- lvh 7y agoI agree that's an optimization and an important litmus test, but calling any TCO that doesn't also handle mutual recursion (but does naive fib just fine) not "real" feels like a stretch. At least we agree on the facts: if you want mutual recursion in Clojure you need to write it with letfn + trampoline. That feels like it's only a hop away from recur to me, as in: if recur doesn't feel like real TCO to you I imagine letfn + trampoline won't either, but letfn+trampoline means that I can write all the mutual recursion things I want to write almost exactly the way I want to write them, so it's close enough for me.
- hajile 7y agoCommon Lisp doesn't require tail-call support (though some implementations have it).
- DannyB2 7y agoWhile tail call is an optional feature in CL, are there any (or very many) implementations that DO NOT have tail call elimination? (just curious)
- kazinator 7y agoTail recursion can be cobbed together with macros. This is particularly effective for recursion among local functions in the same lexical scope: http://www.kylheku.com/cgit/lisp-snippets/tree/tail-recursion.lisp http://www.kylheku.com/cgit/lisp-snippets/tree/tail-recursio... The above tail recursion macros achieve local tail calls by transforming to a tagbody with go. This is offered in the form of a tlet macro whose syntax is like labels. Write your mutually tail recursive functions as labels, then change labels to tlet to try it with this. tlet is based on argtags: a form of tagbody whose labels take arguments. These arguments perform a re-assignment of local variables from parameters that accompany the goto transfer. Tail calls among top-level functions are supposed by a complementary facility called deftail which uses a combination of non-local dynamic control transfers and a dispatch trampoline.
- kbp 7y ago> Clojure has cons, car, cdr which you can use in the classic Lisp sense. The only thing you CAN'T do is have a dotted pair or dotted list in the last cons cell. So, it doesn't have them in the classic Lisp sense. Conses are just pairs. Using them as such isn't exotic. (cons 1 2) being an error in Clojure isn't a minor thing, it's very unique compared to other Lisps. It has a very different definition of cons.
- DannyB2 7y agoYes. Clojure doesn't have cons cells in the traditional sense. But as long as you don't need dotted cdrs, you can "think" of using cons, car, cdr in the ordinary way. You can use existing lisp idioms of recursively taking apart, editing and rebuilding a complex form.