Live data from Hacker News

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

mgmarlow.com

121–130 of 146 posts

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

#121
post #118
post #101

Earlier quoted context omitted.

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 th…

I don't quite get the argument. Isn't this similar to e.g requiring the `z` parameter to be annotated with something that ensures that it is a small number? ("What is the downside of having special syntax for that?") My undrstanding is that a tail call is a tail call , and the variant with +1 is not that, thus being a candidate to be looked at. Perhaps also IDEs can help here if it's difficult to spot?

> Isn't this similar to e.g requiring the `z` parameter to be annotated with something that ensures that it is a small number?

You mean something like "unsigned short int" :) ? Another example would be "const". What's the point - either the variable is const or it isn't. Programmer can just remove the mutation.

> My undrstanding is that a tail call is a tail call, and the variant with +1 is not that, thus being a candidate to be looked at.

Sure, but don't you agree it's an easy mistake to make? Especially when the recursion goes through several different functions. Additionally - you reading the code might not realize it's important for this function to continue to be tail-recursive.

> Perhaps also IDEs can help here if it's difficult to spot?

Or perhaps a compiler can do it?

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

#122

I've seen a lot of chatter about tail calls, but I don't think I've ever seen actual examples of what they look like. Does anyone use them or is it just something for language nerds to obsess about?

One way to look at it is they let you do "low level functional programming", where you can write code that jumps around without using stack space.

If you think about all the standard statements in a language - if, while, async, etc - all of these kinds of constructs can be built out of normal functions once you have tail calls. This makes the language more extensible and reduces the need to introduce new kinds of built-in language constructs and allows more experimentation.

They're really useful for input processing/parsing, recursion, implementing algorithms, etc.

Another way to think about it is that functions can become little 'named blocks' with defined inputs and outputs, and you can combine them together without worrying so much about the runtime costs of functions that take up stack space. So if you have a function with lots of if conditions, while loops, etc, you can convert it into these 'named blocks' instead, which makes maintenance easier.

There are lots of examples. In a way, the fact that none of the main imperative languages support this simple runtime feature is what's stopping people from realising how great tail calls are. There's a bit of a Catch-22 happening.

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

#123
post #11

One advantage of syntactic tail calls is that you can give an error if you are unable to transform to a tail call. Otherwise, you could have a program that seems to work file, and then you refactor and now your recursion isn’t a tail call anymore, and your stack blows up.

Perhaps with a syntax for tail calls, you could do it not at the end of a function.

It needs to be at the end of the calling function so you can throw the calling function's stack frame away, since it's still in use. Getting rid of the calling stack frame is what proper tail calls is about.

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

#124
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 collections wasn't useful because it omitted free() calls, but because it allowed the design of flows with many exit points

This misses the mark a bit. C++ destructors allow for multiple exits. Design at the level of an individual subroutine is not very important, ultimately.

Garbage collection is useful because it enables greater modularity, at the level of full applications and protocols. It does not force details of memory management and ownership into api contracts, leaving them free to be changed. (This, incidentally, is one of the reasons smarter people than I take issue with rust: it forces details of ownership into api contracts, reducing modularity. It is also the reason why reference counting is less modular than tracing gc: it does not deal with cycles, so cyclicality is part of api contracts, and a very subtle part at that.)

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

#125
post #112
post #89

Earlier quoted context omitted.

I disagree. My experience with codebases with programmers who program like that is that you eventually get memory leaks, some of which are near-impossible to debug or fix. You pay for that kind of thinking down-the-line. Enabling new design patterns == good Not thinking about what happens under the hood == bad Catastrophes are rare, but expensive enough to cost more than thinking things through.

Where do memory leaks appear more often, in C, or in Python?

Memory leaks appear in BOTH (specifically, any data structure that accidentally keeps references to stuff will leak memory in python). The biggest difference is that Python won't create the particularly bad/dangerous memory leaks (eg, use after free).

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

#126
post #115
post #111

Earlier quoted context omitted.

The downside is that most web developers don't care about performance, so they wouldn't bother to use it.

TCO is kinda binary. If you use a recursive function on small data it doesn't matter. If you use it on big data it fails with stack overflow so they will fix it.

What if you're using it on small data, but inside a loop?

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

#127
post #94
post #72

Earlier quoted context omitted.

> I don't really know what the utility of TCO/proper tail calls would be. You already have UX constraints that nudge to avoid having a ton of stuff on the screen. The only thing you can think of using recursion for is walking the DOM?

Not specifically walking the DOM but doing DOM/UI related stuff with JS is where I sometimes use recursion. This might also be data processing, but that data is often small and shallow enough for stack growth not not matter, because it is typically related to the UI in _some_ way. I don't typically use JS for heavy computation of large datasets that lend themselves to recursion. The only thing I can think of that is…

Would be nice to be able process huge arrays recursively.

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

#129
post #28

Earlier quoted context omitted.

> Any time a function ends by calling another function and doesn't do anything else besides perhaps return the value you're using a tail call. It's just the name for a function call that's in tail position It sounds like you already know this, but many people, including myself at one time, think of tail call optimization as a trick for not blowing up the stack when writing recursive functions. However, it's much more…

For me the mental leap was that when I call a function, what I'm saying is "do this, then come back to me so I can finish." What if I don't care if the function comes back, because I've already done all the work I need to do? What if I'm really just handing off my results to the function for it to finish the job itself? Then I should give the function the address of whoever called me, and leave. The function I'm call…

What you’re describing is a continuation. And yes that’s a fine mental model for TCO.

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

#130
post #125
post #112

Earlier quoted context omitted.

Where do memory leaks appear more often, in C, or in Python?

Memory leaks appear in BOTH (specifically, any data structure that accidentally keeps references to stuff will leak memory in python). The biggest difference is that Python won't create the particularly bad/dangerous memory leaks (eg, use after free).

Use after free is not a consequence of memory leaks. A memory leak is, specifically, memory which is still allocated but not referenced. Use after free errors can't happen if you don't free the memory.
Post reply on HN