Live data from Hacker News

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

mgmarlow.com

111–120 of 146 posts

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

#111
post #101
post #91

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…

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)

#112
post #89

Earlier 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.

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

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

#113
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.

I agree, syntactic tail calls seems like a great compromise if automatic tail calls are too risky. I'd love to have them in the language.

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)

#114
post #92
post #88

Earlier 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.

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.

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

#115
post #111
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…

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.

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

#116
post #89

Earlier 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 my experience, memory leaks in GC languages are very easy to track down, since the timing for analyzing the heap is excellent. While discovering that you do have a memory leak is not very easy, once you know about it you can easily compare heap snapshots, find the increasing objects, and track down their GC roots, all from the same tool. With Java especially, you can even do this on a running service in production, with minimal downtime.

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)

#117
post #114
post #92

Earlier 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.

It's SQL inspired language, because SQL is far and away more well known, and understandable, than functional programming.

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)

#118
post #101
post #91

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…

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?

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

#119
post #2

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

The spec for STC has a critique of PTC:

- 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

Post reply on HN