nodejs / CTC

Node.js Core Technical Committee & Collaborators
80 stars 27 forks source link

Tail call optimiztion (TCO) in ES6 & Node.js #3

Closed rvagg closed 8 years ago

rvagg commented 8 years ago

There is some concern about TCO and how it plays out in VMs and it'd be worth us being aware of this so we don't get caught off guard. Thanks to @ofrobots for starting this discussion.

I believe that the almost all stakeholders involved in the discussion till now have been browser-focused, which is also true of many other things coming out of TC39. This is not to assign fault and it's actually very understandable and while I think we are seeing positive signs of greater Node.js representation, both from within and new blood from outside, because we don't have a VM, we don't have as clear a seat at the table on these matters as do the other big influencers.

So, it's important that we at least have this discussion so that we know what the issues are and have a general sense of a Node.js position (I doubt we'll come up with a Node.js position) that can be heard by TC39 and VM implementers.

/cc @bmeck @ljharb

benjamingr commented 8 years ago

V8 has in fact implemented tail call optimization although many things cause a deopt https://bugs.chromium.org/p/v8/issues/detail?id=4698

ofrobots commented 8 years ago

Implicit tail-call-optimization is part of ES6. So far only Apple is shipping this as part of their Safari tech previews. V8 has already implemented this, but has been holding back on shipping. As far as I understand Edge and Firefox have not implemented this yet, but that may change.

At the last TC39 meeting the committee failed to reaches consensus on removing implicit tail-call optimization from the spec. This leaves the future of TCO very uncertain. If a second vendor decides to ship this; that would essentially force every vendor to have to implement and ship.

It is my opinion that TCO would quite detrimental to the Node.js ecosystem. It breaks stack traces and debugging productivity for existing code in addition to making it much harder to do in-production profiling or post-mortem debugging. Others prominent Node users have similar views.

As @rvagg mentions, much the discourse on this topic has been driven from the web use cases. I think it would be important of Node.js stakeholders to be aware of the implications if TCO ends up shipping. It would be good to provide feedback to the implementers, one way or they other. The worst thing would be if this comes down the implementation pipe without due feedback from the Node.js community.

jasnell commented 8 years ago

I've been poking around the edges of this for a few weeks just to keep an eye on it. While TCO is one of those things that sounds great in theory and likely would be quite good for the browser environment, I agree that it presents a fundamental challenge for Node.js. My question would be: would it be possible to implement it in a way that Node.js can simply switch it off or would it be something we'd be forced to accept?

sam-github commented 8 years ago

Lua has similar semantics as the es6 proposal, if the final statement is a return fun() it reuses the frame, which is fine, it always had it, and devs expect it. However, it did interact poorly with debugging sometimes, and would make calls "disappear" from stack traces:

function a() { return b() }
function b() { return c() }
function c() { console.trace() ..// would not include b()!

in lua, when we cared, you'd rewrite b as

function b() {
  c()
  return
}

to evade the optimization

Anyhow, TCO is a great feature, nice for algorithms and state machines, but when retroactively applied to existing js code, I think its likely to cause confusion.

Ugly though it is, es6 should probably adopt a keyword to make TCO something that happens on explicit request, not an optimization that is surprisingly applied, sometimes, by some browsers.

ofrobots commented 8 years ago

Ugly though it is, es6 should probably adopt a keyword to make TCO something that happens on explicit request

This ('Syntactic Tail Calls') was proposed at TC39. And shot down.

Anyhow, TCO is a great feature, nice for algorithms and state machines, but when retroactively applied to existing js code, I think its likely to cause confusion.

My understanding is that the original reason for adding TCO was for transpiling other (perhaps functional) languages (that expect this behaviour from the implementation) to JavaScript. Since then the use-case has evaporated because of WebAssembly.

My understanding of the current status is that, 'TCO might be nice for some programmers, debugging for existing code does get worse, implementing this will be hard for some implementers. Regardless, this is part of the spec, so it must be shipped.'

benjamingr commented 8 years ago

I don't understand, TCO could preserve stack traces and your code could opt-out when a debugger is attached.

Why would this be a problem?

ljharb commented 8 years ago

@ofrobots the important question here is, would explicit PTC via syntax (with nothing forbidding implicit PTC), instead of mandated implicit PTC, solve your concerns? Or is the only good outcome for you no PTC in the language at all?

(Please note, this is not about tail call optimization at all - PTC is not an optimization for performance, it's only a guarantee about available stack space - TCO is not a thing that currently exists)

ofrobots commented 8 years ago

I don't understand, TCO could preserve stack traces and your code could opt-out when a debugger is attached.

This comment by @yunong explains why 'opting out' is not an option for people running Node.js on servers.

@ofrobots the important question here is, would explicit PTC via syntax (with nothing forbidding implicit PTC), instead of mandated implicit PTC, solve your concerns?

Actually, I am going to try to refrain from giving more opinions of my position. I think this thread is supposed to be about whether Node.js has concerns with tail call elision (one way or the other). The opinion of the CTC matters here, not mine.

ljharb commented 8 years ago

OK thanks - I just wanted to clarify whether it was any form of tail call elision, or only the implicit form, that was potentially problematic.

Fishrock123 commented 8 years ago

@ljharb pretty sure explicit, but that could make it hard to debug a module that you do not know is using them.

In the event of that, a flag could probably be added to turn them off for debugging?

ljharb commented 8 years ago

An implementation-level flag could be used to turn off any form of PTC, absolutely.

I'm happy to bring the CTC's concerns to TC39 in July, fwiw, I just want to attempt to pre-answer their questions :-)

jasnell commented 8 years ago

A flag turning them off for debugging would make it rather difficult to debug issues that only creep up when they are being used. Personally, I'd rather have the option of turning it off/on at compile time, then shipping Node.js with it off by default until we have a better handle on what the overall impact and support strategy would be.

rvagg commented 8 years ago

Moar discussion going on @ https://github.com/kangax/compat-table/issues/819

Fishrock123 commented 8 years ago

Implicit TCO seems really bad to me if it breaks expectations of debugability without specifically opting in...

Trott commented 8 years ago

If I understand the situation, this seems like it is unlikely to be resolved soon and unlikely to ship in V8 soon, and possibly ever. I'm going to close this, as I imagine should this become something that will ship in V8, @ofrobots, @ljharb, and probably many others would sound the alarm. But in the meantime, I'm not sure that having this issue open provides any value. Feel free to re-open if you disagree. (I'm just doing some issue-tracker janitorial work and making judgment calls.)