6 ms·
> Lua has a crucial feature that Javascript lacks: tail call optimization. I'm not familiar with Lua, but I expect tco to be a feature of the compiler, not of
by teo_zero 10mo ago
> Lua has a crucial feature that Javascript lacks: tail call optimization.
I'm not familiar with Lua, but I expect tco to be a feature of the compiler, not of the language. Am I wrong?
- naasking 10mo agoIf the language spec requires TCO, I think you can reasonably call it part of the language.
- teo_zero 10mo agoIt wouldn't be the first time the specs have gone too far and beyond their perimeter. C's "register" variables used to have the same issue, and even "inline" has been downgraded to a mere hint for the compiler (which can ignore it and still be a C compiler).
- tracker1 10mo agoIIRC, ES6+ includes TCO, but no actual implementation/engine has implemented it.
- graftak 9mo agoSafari has
- kerkeslager 10mo agoI don't think you're wrong per se. This is a "correct" way of thinking of the situation, but it's not the only correct way and it's arguably not the most useful. A more useful way to understand the situation is that a language's major implementations are more important than the language itself. If the spec of the language says something, but nobody implements it, you can't write code against the spec. And on the flip side, if the major implementations of a language implement a feature that's not in the spec, you can write code that uses that feature. A minor historical example of this was Python dictionaries. Maybe a decade ago, the Python spec didn't specify that dictionary keys would be retrieved in insertion order, so in theory, implementations of the Python language could do something like: >>> abc = {} >>> abc['a'] = 1 >>> abc['b'] = 2 >>> abc['c'] = 3 >>> abc.keys() dict_keys(['c', 'a', 'b']) But the CPython implementation did return all the keys in insertion order, and very few people were using anything other than the CPython implementation, so some codebases started depending on the keys being returned in insertion order without even knowing that they were depending on it. You could say that they weren't writing Python, but that seems a bit pedantic to me. In any case, Python later standardized that as a feature, so now the ambiguity is solved. It's all very tricky though, because for example, I wrote some code a decade that used GCC's compare-and-swap extensions, and at least at that time, it didn't compile on Clang. I think you'd have a stronger argument there that I wasn't writing C--not because what I wrote wasn't standard C, but because the code I wrote didn't compile on the most commonly used C compiler. The better approach to communication in this case, I think, is to simply use phrases that communicate what you're doing: instead of saying "C", say "ANSI C", "GCC C", "Portable C", etc.--phrases that communicate what implementations of the language you're supporting. Saying you're writing "C" isn't wrong, it's just not communicating a very important detail: what implementations of the compiler can compile your code. I'm much more interested in effectively communicating what compilers can compile a piece of code than pedantically gatekeeping what's C and what's not.
- kristianp 10mo agoAre you saying that Lua's TCO is an accidental feature due to the first implementation having it? How accurate is that?
- mananaysiempre 10mo agoYou’re wrong in the way in which many people are wrong when they hear about a thing called “tail-call optimization”, which is why some people have been trying to get away from the term in favour of “proper tail calls” or something similar, at least as far as R5RS[1]: > A Scheme implementation is properly tail-recursive if it supports an unbounded number of active tail calls. The issue here is that, in every language that has a detailed enough specification, there is some provision saying that a program that makes an unbounded number of nested calls at runtime is not legal. Support for proper tail calls means that tail calls (a well-defined subgrammar of the language) do not ever count as nested, which expands the set of legal programs. That’s a language feature, not (merely) a compiler feature. [1] https://standards.scheme.org/corrected-r5rs/r5rs-Z-H-6.html#%_sec_3.5 https://standards.scheme.org/corrected-r5rs/r5rs-Z-H-6.html#...
- teo_zero 10mo agoThank you for the precise answer. I still think that the language property (or requirement, or behavior as seen by within the language itself) that we're talking about in this case is "unbounded nested calls" and that the language specs doesn't (shouldn't) assume that such property will be satisfied in a specific way, e.g. switching the call to a branch, as TCO usually means.
- mananaysiempre 10mo agoUnbounded nested calls as long as those calls are in tail position, which is a thing that needs to be defined—trivially, as `return EXPR(EXPR...)`, in Lua; while Scheme, being based around expressions, needs a more careful definition, see link above. Otherwise yes. For instance, Scheme implementations that translate the Scheme program into portable C code (not just into bytecode interpreted by C code) cannot assume that the C compiler will translate C-level tail calls into jumps and thus take special measures to make them work correctly, from trampolines to the very confusingly named “Cheney on the M.T.A.”[1], and people will, colloquially, say those implementations do TCO too. Whether that’s correct usage... I don’t think really matters here, other than to demonstrate why the term “TCO” as encountered in the wild is a confusing one. [1] https://www.plover.com/misc/hbaker-archive/CheneyMTA.html https://www.plover.com/misc/hbaker-archive/CheneyMTA.html