Earlier quoted context omitted.
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 th…
What happened to proper tail calls in JavaScript? (2021)
111–120 of 146 posts
Re: What happened to proper tail calls in JavaScript? (2021)
#112Earlier quoted context omitted.
On a tangent, I've always felt garbage collection, and, more importantly, safe memory management, was important because it allows you to mostly pretend that memory allocation isn't a globally side-effecting operation, as such operations are difficult to reason about. It's the same logic that leads to design choices that don't explicitly use global variables or global state. In reality, all memory allocation is still…
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.
Re: What happened to proper tail calls in JavaScript? (2021)
#113So, 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.
Here are some strawman syntax examples from the Syntactic Tail Calls proposal.
Return Continue
function factorial(n, acc = 1) {
if (n === 1) {
return acc;
}
return continue factorial(n - 1, acc * n)
}
let factorial = (n, acc = 1) => continue
n == 1 ? acc
: factorial(n - 1, acc * n);
// or, if continue is an expression form:
let factorial = (n, acc = 1) =>
n == 1 ? acc
: continue factorial(n - 1, acc * n);
Function sigil // # sigil, though it's already 'claimed' by private state.
#function() { /* all calls in tail position are tail calls */ }
// Note that it's hard to decide how to readably sigil arrow functions.
// This is probably most readable.
() #=> expr
// This is probably most in line with the non-arrow sigil.
#() => expr
// rec sigil similar to async functions
rec function() { /* likewise */ }
rec () => expr
!-return function () { !return expr }
// It's a little tricky to do arrow functions in this method.
// Obviously, we cannot push the ! into the expression, and even
// function level sigils are pretty ugly.
// Since ! already has a strong meaning, it's hard to read this as
// a tail recursive function, rather than an expression.
!() => expr
// We could do like we did for # above, but it also reads strangely:
() !=> expr
https://github.com/tc39/proposal-ptc-syntax#syntax-alternati...Re: What happened to proper tail calls in JavaScript? (2021)
#114Earlier quoted context omitted.
The major thing Python has done is bring major functional design patterns, like maps, reductions, everything in itertools, and made human-friendly versions and syntax for them. Right now, that covers 70% of the stuff I did in Scheme over C/C++ (weighed by volume of use, rather than by feature list). A lot of beginner programmers now use features like closures and decorators, and they're accessible. The remaining 30%…
I would argue that, as far as popularizing sequence comprehensions goes, C# (LINQ) was probably more influential than Python.
Re: What happened to proper tail calls in JavaScript? (2021)
#115Earlier 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…
The downside is that most web developers don't care about performance, so they wouldn't bother to use it.
Re: What happened to proper tail calls in JavaScript? (2021)
#116Earlier quoted context omitted.
On a tangent, I've always felt garbage collection, and, more importantly, safe memory management, was important because it allows you to mostly pretend that memory allocation isn't a globally side-effecting operation, as such operations are difficult to reason about. It's the same logic that leads to design choices that don't explicitly use global variables or global state. In reality, all memory allocation is still…
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.
In contrast, it's much harder to track down memory leaks in C, since the runtime has little information about the contents of the heap. You typically end up using valgrind to instrument your code, assuming it can still run fast enough to reproduce the problem.
The only exception is Go, which has awful tooling for analyzing memory. For some bizarre reason, it can't even show you the gc roots of an object in memory, even though the GC obviously needs this information to work properly.
Re: What happened to proper tail calls in JavaScript? (2021)
#117Earlier quoted context omitted.
I would argue that, as far as popularizing sequence comprehensions goes, C# (LINQ) was probably more influential than Python.
LINQ weirdly renamed basic FP concepts. Why is the map operation called “Select”? If anything, I'd expect the filter operation to be called that.
And that is one reason why LINQ is still one of the only functional collections APIs that exposes GroupBy, which is an extremely useful operation.
Edit: correction, LINQ is no longer the only one that introduced GroupBy, but it was probably the first.
Re: What happened to proper tail calls in JavaScript? (2021)
#118Earlier quoted context omitted.
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 th…
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?
Re: What happened to proper tail calls in JavaScript? (2021)
#119Reads like a sad state of affairs, but the article itself doesn't really explain whatbthe actual concerns where that caused the proposals to be put on ice. From reading, I mostly get "PTC was un-implemented and put on ice because some browser vendors had issues with it; the alternative proposal, STC, was put on ice because other browser vendors had different issues with it. Then everyone (from the browser vendor side…
- performance
- developer tools
- Error.stack
- cross-realm tail calls
- developer intent
See: https://github.com/tc39/proposal-ptc-syntax#issues-with-ptc
Apple's 2016 response as to why they won't implement STC is here: https://github.com/tc39/ecma262/issues/535
- STC is part of the spec and will take too long to change.
- Now that they've implemented support for PTC, they don't want to regress web pages that rely on it.
- They don't want to discourage vendors from implementing PTC by agreeing to STC.
- They don't want to introduce confusion.
Some of these arguments about confusion and delays seem wrong hindsight, since on every point things would have been better if they'd just agreed to the compromise of STC.
- It would have been part of the spec years ago
- STC would have had a clear way for web pages to know when tail calls could be relied on (and PTC would have been optional)
- Other vendors didn't implement PTC in any case, despite no agreement on STC
- There's even more confusion as things are now