Live data from Hacker News

What happened to proper tail calls in JavaScript? (2021)

mgmarlow.com

101–110 of 146 posts

Re: What happened to proper tail calls in JavaScript? (2021)

#101
post #91
post #86

Earlier quoted context omitted.

Goto can be anything - a function call, a loop, an if clause, exception handler. That's the whole reason we created structured programming - so we don't have to guess what it is THIS time. Tail call optimization is nice, but it has its problems. What's the downside to requiring special syntax for it? It frees the programmer reading the code from determining if it's a tail call every time.

Right, but optimizing tail calls won't lose that structure. But sure, just using goto is a slippery slope to confusion.

Optimizing calls using the same syntax as regular ones risk introducing stack overflow without noticing.

    function rec(x, y, z) {
       if (something(z)) {
          return rec(x, y, z-1);
       }
       return 1;
    }
Now you need to add 1 to the result.

    function rec(x, y, z) {
       if (something(z)) {
          return rec(x, y, z-1) + 1;
       }
       return 1;
    }
Ups, now it fails with stack overflow for big z values. Hope you have good unit tests to catch this.

To avoid this problem whenever you modify a possibly recursive function in a language with transparent TCO - you have to look through the code to see if it's using TCO. This is wasted time. And it can be non-trivial if you have recursive functions calling each other and other funny stuff.

If instead the language required special syntax for TCO:

    function rec(x, y, z) {
       if (something(z)) {
          return TAIL_CALL rec(x, y, z-1) + 1;
       }
       return 1;
    }
This would be a compilation error cause it's not a tail call contrary to the declaration.

What is the downside to requiring special syntax?

Re: What happened to proper tail calls in JavaScript? (2021)

#102
post #6

Earlier quoted context omitted.

[From the article] Why are browser vendors ignoring PTC? V8 chalks it up to two main reasons: * It makes it more difficult to understand during debugging how execution arrived at a certain point since the stack contains discontinuities, and * error.stack contains less information about execution flow which may break telemetry software that collects and analyzes client-side errors.

> It makes it more difficult to understand during debugging how execution arrived at a certain point since the stack contains discontinuities That's a weird complaint, considering that stacks don't describe "how execution arrived at a certain point". In fact, stacks don't describe the past at all; rather, they describe the future of what's left to do (AKA the "continuation"). For example, consider this code: function…

For execution, a stack is a continuation. For debugging, we pretend like it's a historical record, and mostly get away with it. Various things break the correspondence slightly. TCO breaks it a lot more.

Debugging is important. It doesn't get enough respect. Stacks are a pretty critical component of debugging, for better or worse.

It would be great if we didn't depend on this fiction quite so much. With native code, there are definitely alternative options now, such as rr[1] and Pernosco[2] where if you want to look back in time—well, you just go back in time. For JavaScript, that's becoming more and more possible with things like Replay[3]. Perhaps before long, the debugging argument will just go away.

[1] https://rr-project.org/

[2] https://pernos.co/

[3] https://www.replay.io/

Re: What happened to proper tail calls in JavaScript? (2021)

#103
post #54
post #22

So, what happened to syntactic tail calls? I think that's what I would prefer anyway, both because it makes it more clear from a debugging standpoint, since you opt in, and because you can get a warning (or compiler/linter error if using a transpiler or linter) when your function isn't actually tail recursive.

The whole "issue" is very strange to me. Proper tail calls (PTC) without the extra syntax are literally free performance boosts for existing code. The whole "stack frames" argument is a red herring: * Nobody expects stack frames to exist for every `for` loop which is the biggest practical use for PTC * Stack frames go away the second you release control back to the event loop which is by far the more pernicious probl…

> The whole "issue" is very strange to me. Proper tail calls (PTC) without the extra syntax are literally free performance boosts for existing code.

It's bad to create a dangerous performance cliff. There could be some TCO based code that works fine, and then a junior coder makes an 'innocent' change that makes it ineligible for TCO, then that code eventually starts getting OOM crashes on heavy data.

I think if they're gonna do TCO then it really should be syntactic. If someone is writing their code around an assumption of TCO, then they almost always want an explicit guarantee of TCO, and they want to fail fast if TCO isn't happening.

Re: What happened to proper tail calls in JavaScript? (2021)

#104
post #96
post #73

Earlier quoted context omitted.

People do a lot more in JavaScript than you realize.

That's what I'm wondering about. When was the last time you blew the stack with JS and what did you try to accomplish? Another commenter said they had problems walking a dependency graph.

For some examples: People have 3D game engines running in JavaScript. There's a lot of cryptographic work in JavaScript, including but not limited to cryptocurrencies - a lot of groundbreaking stuff from a technological perspective. Full blown emulators, developer tools, virtual machines... the world of JavaScript is way larger than CRUD applications.

Re: What happened to proper tail calls in JavaScript? (2021)

#105
post #78

Earlier quoted context omitted.

> Stack frames go away the second you release control back to the event loop which is by far the more pernicious problem. Sadly the opposite is true today: If you're doing async/await programming in Chrome (and Firefox too, I think?) the runtime actually tries to carry your stack across event loop turns and this will be visible in Error.stack. This happens even with the debugger closed in my experience (the massive s…

There are times where they are very useful.

Having had to debug issues in unfamiliar async-using code without the benefit of that feature (Now who the hell originally called this function?), I'd tend to agree.

Re: What happened to proper tail calls in JavaScript? (2021)

#106
post #76
post #34

Earlier quoted context omitted.

No, he's right. It got 'demoted' from a core function to a footnote in a module. See also https://blog.finxter.com/about-guidos-fate-of-reduce-in-pyth...

No he's wrong. Python the language and Python the standard library are not separate things.

no I'm right

> So in my mind, the applicability of reduce() is pretty much limited to associative operators, and in all other cases it's better to write out the accumulation loop explicitly.

https://www.artima.com/weblogs/viewpost.jsp?thread=98196

Re: What happened to proper tail calls in JavaScript? (2021)

#107
post #54

Earlier quoted context omitted.

The whole "issue" is very strange to me. Proper tail calls (PTC) without the extra syntax are literally free performance boosts for existing code. The whole "stack frames" argument is a red herring: * Nobody expects stack frames to exist for every `for` loop which is the biggest practical use for PTC * Stack frames go away the second you release control back to the event loop which is by far the more pernicious probl…

> The whole "issue" is very strange to me. Proper tail calls (PTC) without the extra syntax are literally free performance boosts for existing code. It's bad to create a dangerous performance cliff. There could be some TCO based code that works fine, and then a junior coder makes an 'innocent' change that makes it ineligible for TCO, then that code eventually starts getting OOM crashes on heavy data. I think if they'…

This is simply untrue in practice.

Almost 52% of mobile web traffic in the US is iOS/Safari which implements proper tail calls. Despite this, we don't get constant stack overflows.

A little more than 1 in 9 use desktop Safari which also implements proper tail calls. We also don't see stack overflow issues here either.

Re: What happened to proper tail calls in JavaScript? (2021)

#108
post #68

For the most part, programming constructs like these split the world into two camps: - People who point out things can be done without them, who largely see them as useless due to lack of familiarity - People who've used them, and see critical ways to restructure code to make it cleaner using said constructs That's been the case for a lot of progress in programming. Python, and ES2015, have done a wonderful job of br…

Garbage collection was seen as garbage by C/C++ programmers ("What's the big deal with adding a free() call? Stupid lazy people.") As a former C++ programmer working on a large code base when/before Java was introduced, garbage collection was seen as expensive and slow, but definitely not a waste of time. I would have given my right arm to be free (pun intended) from the need to manually manage my heap across threads…

exactly, it was never about attitude, it's about deterministic real-time. C/C++ are low level systems languages for real-time control applications. In that case garbage collection, a non-deterministic unpredictable stop-the world pause, simply is out. so you have to have a base language without GC. you can use garbage collection via libraries in c/c++ if you don't need deterministic real time. It never was about "not liking" GC or "thinking it's garbage".

Re: What happened to proper tail calls in JavaScript? (2021)

#109
post #57

Earlier quoted context omitted.

How can this be bad or a footgun? If the algorithm can be PTC optimized, then it is and everything works as efficiently as possible. If not, then it blows the stack either way. Finally, a trampoline is objectively worse. The programmer has to have an even bigger understanding of tail calls. Trampolines involving complex patterns are MUCH more difficult to follow. The trampoline is implemented in JS rather than C++. T…

> How can this be bad or a footgun? You are coming from the side of someone who already has a a well planned algorithm that doesn't get stuck in infinite looping or end up diving too deep. My concern was for those without a well planned algorithm, who don't see that it can get stuck in a loop or dives too deep too quickly. In these situations blowing your stack is a good indication you have a problem. This is just my…

52% of mobile traffic runs on iOS which implements PTC, but the world doesn't end. 1 in 9 desktops use Safari which also implements PTC without issue.

They've been using PTC since 2016 as I recall and all the complaints that the world would break simply haven't happened.

Re: What happened to proper tail calls in JavaScript? (2021)

#110
post #86
post #79

Earlier quoted context omitted.

A tail call is just a goto; using a special syntax for it (`for…` or `while…`) is simply syntactic sugar. Nobody complains about the lack of a stack frame in a for loop.

Goto can be anything - a function call, a loop, an if clause, exception handler. That's the whole reason we created structured programming - so we don't have to guess what it is THIS time. Tail call optimization is nice, but it has its problems. What's the downside to requiring special syntax for it? It frees the programmer reading the code from determining if it's a tail call every time.

The downside is that your code doesn't optimize for free.

iOS is over 50% of the mobile browser share in the US and desktop Safari is 11-12% (roughly 1 in 9) and both implement proper tail calls and have for around 6 years now. Despite this, the world has not collapsed and people aren't constantly complaining that websites are constantly breaking.

Post reply on HN