4 ms·
This is not a Clojure problem, it's a JVM problem--no tail call optimization. Armed Bear Common Lisp has the exact same limitation.
by kyllo 11y ago
This is not a Clojure problem, it's a JVM problem--no tail call optimization. Armed Bear Common Lisp has the exact same limitation.
- eru 11y agoAnd didn't tail call optimization only become standard in Lisps with the advent of Scheme? In any case, at Standard Chartered we didn't have tail call optimization with our Haskell dialect either. It didn't matter too much in practice, because you should be using combinators anyway. And when you are calling foldr or map, you do not care that somewhere hidden away they are implemented with a loop in C++, as long as they behave right.
- jfoutz 11y agoYup. The CL spec does not require TCO. Some implementations only do TCO on self calls, so no mutual recursion. And really i think the "Classic" lisp way to loop is loop, not recursion. Scheme forces the point and requires full TCO.
- hga 11y agoAnd really i think the "Classic" lisp way to loop is loop, not recursion. Both, I think (from long ago memories). The "modern" loop macro (which is probably Turing complete like I seem to remember people saying format is :-) is I think a relatively new thing, I overhead a lot of discussion about its design in the early '80s. Although it probably had precursors, mainline Lisp, now Common Lisp, is decidedly multi-paradigm, there were even sops thrown to FORTRAN programmers as I recall, probably back from when there were only a very few computer languages in existence (heck, LISP's first implementation, on a vacuum tube computer, was as FORTRAN subroutines). So overt things like loop are in theory idiomatic as well as recursion.
- lispm 11y agoMost CL implementations will do more TCO than on self calls or mutual recursion. Most support full TCO, with language limitations. The ones that don't provide any form of TCO are old Lisp Machine implementations and ABCL on the JVM. SBCL, OTOH, provides full TCO. Some older overview about Common Lisp implementations and their TCO support: http://0branch.com/notes/tco-cl.html http://0branch.com/notes/tco-cl.html
- ohyes 11y agoI seem to recall it depending on compiler settings whether sbcl does tco. Which means you probably don't want to rely on it in general unless you are okay being locked to a specific implementation and specific optimization settings that may-or-may not seem magical to the uninformed user.
- lispm 11y agoThere is Common Lisp software which needs TCO and only runs in TCO supporting implementations.
- hga 11y agoIndeed, and as far as I know Scheme JVM implementations, which I haven't looked at in a long while, either do it per the spec and are slow, SISC reputedly, died about the time I might have started using it, or go through contortions like Kawa to do the best you can. Don't know about JScheme, it was dead before then, and I just noticed Bigloo will compile to the JVM, adding one to the list of 4.
- kyllo 11y agoWell SC's proprietary compiler is probably different, but GHC is self-hosted (the runtime system is C but the compiler is implemented in Haskell) and the map and fold functions in Prelude are recursive. Here's the source code for `map` in Prelude: map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x : map f xs But it is definitely still true that explicit recursion is discouraged as being too 'low-level' for most Haskell code and it's preferable to use higher-order functions instead.
- eru 11y agoYes, Standard Chartered's compiler a bit different from ghc. That's mostly for historical reasons. Yes, GHC can and does just use the recursive goodness, and compile it away to no-stackframe-adding jumps.
- SeanLuke 11y agoWait, what? Kawa does tail call optimization just fine. And it's a JVM language. https://www.gnu.org/software/kawa/Restrictions.html https://www.gnu.org/software/kawa/Restrictions.html
- Volundr 11y agoThe --full-tail-calls flag is not on by default, partly because it is noticably slower (though I have not measured how much), and partly I think it is more useful for Kawa to be compilatible with standard Java calling conventions and tools. Well, for some definition of just fine. Well implemented TCO is a performance boost, not hit on most platforms. The lack of TCO built into the JVM means that JVM languages like Scala and Kawa generally have to roll their own on top of the JVM, resulting in a performance hit.
- SeanLuke 11y agoPerformance is irrelevant I think to the discussion. The claim is being made that Clojure and ABCL don't do full tail call elimination because of stack restrictions in the JVM. That sounded unlikely to me. Kawa does full tail call elimination and it's a JVM language. Hence, this claim can't be true, right?
- Turing_Machine 11y agoWell, if you ignore performance, all Turing-complete languages are identical, right?
- Guvante 11y agoKawa fakes it. The JVM doesn't support it so if you use a normal function call you don't get tail recursion. You could avoid calling functions and instead do your own stuff but that doesn't change the fact that the JVM doesn't support it. That is what they mean by performance, you lose performance because you can't do naked function calls in those cases. Since except mutual recursion (which is difficult to detect tail recursion for correctly) the benefit of tail recursion is a performance boost it is ignored when the cost of implementing it kills your performance.