tc39 / ecma262

Status, process, and documents for ECMA-262
https://tc39.es/ecma262/
Other
14.9k stars 1.27k forks source link

Response to the proposal to add explicit tail call syntax to ECMAScript #535

Open msaboff opened 8 years ago

msaboff commented 8 years ago

After some discussion with other JavaScriptCore team members, we at Apple cannot support the suggested change to make tail calls explicit via syntax due to the expected web incompatibilities those changes will create.

Given that tail calls are currently part of the ES6 and draft ES7 specification, a compliant implementation should implement tail calls as described in those specifications. Compliance with any proposed changes would not occur for almost two years since the earliest that any proposed tail call changes could be adopted would be for the ES2017 (ES8?) specification. In the mean time web pages and Javascript applications will be created that are susceptible to future breakage.

In addition to the current JavaScriptCore implementation, it is likely that other browser venders will implement Tail Calls compliant with the current standard. Let’s consider the changes to tail calls as suggested at the March 2016 TC-39 meeting and the impact it would have on Safari and any other compliant implementations that will be shipped this year. I’d like to cover what I understood was being presented at the March meeting, but I will also cover two other reasonable variants of that proposal and my assessment of their suitability.

  1. Tail calls based on tail position are optional. Tail calls based on opt-in syntax are required. There will be web pages and web applications developed starting this year that take advantage of tail calls as currently specified. Those web pages would become susceptible to breakage should we change the specification. Consider web pages that take advantage of tail calls as intended in the current specification. The Javascript written for such a web page assumes unlimited tail calls. Obviously, those pages won't work on an implementation that doesn’t support tail calls. If one browser decides to stop supporting implicit tail call behavior, they break that class of web pages. Therefore early adopters like us need to continue to support the currently specified behavior. The implication of this is that ES6 tail call behavior cannot be made optional without compatibility issues for Safari. Other early adopting browsers have the same issue.

    If another browser decided to not support implicit tail calls, but waits for the proposed change and only implements explicit tail calls, they introduce cross browser incompatibilities. Certainly such a browser is not compatible when running a web page that depends on implicit tail calls and likely never will be. In addition, there would be at least another class of incompatibilities. Consider a web page that inadvertently makes infinite recursive calls. For browsers that waited for explicit tail calls, that web page could throw an out of memory exception that might be silently caught, after which the web page proceeds as expected. For Safari and other browsers that support implicit tail calls, that web page is stuck in an infinite loop. The error is in the web page, but the handling of the error by each browsers is vastly different depending on their support of implicit tail calls. We have seen this type of issue in the past and reduced our stack size to maintain cross browser compatibility.

    Approval of this proposal also encourages browser vendors to wait to implement the current ES6 specified tail call behavior. This selective delay by some implementations diminishes the purpose of the ECMAScript standard and the features contained therein. Selective support by various implementations will cause developers to delay their adoption of the programming patterns tail calls was designed to address. Again this only impacts early adopting browsers like Safari.

    The main problem with this proposal is that it relaxes normative behavior and makes it optional, effectively eliminating that behavior from the standard. After we work through the early adopter problems described above, we’d introduce a new problem: Developers don’t know if they can use tail calls based on tail position or not, since implementations vary. Feature testing for the optional tail call behavior would be difficult and cumbersome, given the current specification. Optional behavior without a feature test has little place in the standard and cannot be relied upon by implementors and developers alike.

    Given these web compatibility issues and related developer concerns, we reject this type of proposal.

  2. Tail calls based on tail position are forbidden. Tail calls based on opt-in syntax are required. A proposal based on this option requires that early adopting implementations, like Safari, need to change and stop supporting implicit tail calls. Those changes break web pages designed for the current spec as described above. It not only breaks those web pages, it would require that they be rewritten. The web breakage and subsequent rework by web developers caused by this option is much worse than with the first option.

    There will also be web pages that accidentally took advantage of and benefited from implicit tail calls. With this proposal, those web pages could break due to running out of stack space. These breakages would be confusing at best to the web page implementers and likely damage the perceived quality of Safari.

    Due to the breakage this imposes on Safari and web pages developed to use implicit tail calls, we also reject this type of proposal.

  3. Tail calls based on tail position are required. New syntax for verifying tail position is required. This is the only proposal that wouldn’t cause breakage. This proposal would be to add syntax to signify a tail call is intended, but wouldn’t change the normative nature of tail calls as currently specified. If the call is not in tail position, a syntax error is issued. Implicit tail calls as currently defined would still be normative and must be implemented by conforming implementations. This change is backward compatible. Pages developed to take advantage of proper tail calls without the new syntax would continue to work. Javascript code developed with the new syntax get additional checking and the added benefit that tail calls appear different in the source code. The usefulness of this change is minimal though as it adds optional syntax with little corresponding semantic changes.

    We recommend against this proposal, but our objection is not as strong as our objections to (1) and (2), since this proposal would not harm web compatibility. Our main concern is that this proposal could add confusion with very little benefit.

concavelenz commented 8 years ago

On Tue, May 3, 2016 at 5:38 PM, Michael Saboff notifications@github.com wrote:

There hasn't been much discussion here. There is a new pull request for Syntactic Tail Calls (STC) https://github.com/tc39/proposal-ptc-syntax.

At this point, there is no reason to support Syntactic Tail Calls given that the three concerns I listed above have not been refuted with provable facts. Here is my understanding of these three concerns.

1.

Performance concerns of PTC.

We now have two implementations that do not see a general performance degradation. As part of the Implement Proper Tail Call [Discussion] https://github.com/Microsoft/ChakraCore/issues/796, there was some discussion that the calling convention implemented in Chakra would require months to change their calling convention to address performance concerns. The JSC team also had to make calling convention changes to implement PTC. Those changes were at least half the effort.

Performance concerns of specific implementations have not been nor should be a criteria for removing or changing a feature in the standard. It seems to me the appropriate time to raise a general or implementation specific performance concern is when a feature is being considered for a new version of the standard. As implementors add new features called out in the current or future ES versions, they often need to make specific optimizations to maintain current performance. Sometimes the new features cannot be added without a performance loss. That has been our experience on the JSC team.

As @othermaciej https://github.com/othermaciej pointed out, STC will not solve any performance issue. Instead it puts the onus on the web developer to decorate calls already in tail position. At best this will delay an implementation from optimizing for Tail Calls. When STC become prominent, all implementations will likely optimize to remain competitive. 2.

Because PTC elide stack frames, debugging would be more difficult.

PTC debug tools discussion on this and other thread express concerns that ECMAScript developers will struggle with PTC due to calls being elided. The JSC doesn't share this concern as there are techniques to include the elided frames. At least two methods have been suggested, keeping a side stack when debugging and the other is turning off PTC when debugging. There may be other methods an implementation could employ to alleviate this concern. At this point, I consider this concern closed. 3.

PTC will eliminate stack frame from telemetry data.

This issue seems to be the most common concern among participants in the PTC discussion. The PTC implementation in JSC has been implemented since October of 2015. Since that time, we have tested PTC internally as well as making the implementation available via WebKit Nightlies https://webkit.org/downloads/, and the Safari Technology Preview https://developer.apple.com/safari/technology-preview/. We have received only one bug report related to PTC. That bug https://bugs.webkit.org/show_bug.cgi?id=156765 turned out to be a locking bug (trying to recursively lock an non-recursive lock) that was triggered by PTC.

It would be helpful if there was a real example of this concern that could be demonstrated with either the Safari Technology Preview, or if PTC can be turned on in the latest Chrome Canary, via that browser. Should PTC compatibility issues come up, proper analysis of those issues should ensue and be dealt with at that time.

I don't believe either of those options would provide any feedback from the Node.js community or from those concerned with collecting data from in-field browsers. What data to you have to show that it won't adversely affect field reports? Perhaps, Apple does the same kind of error collection? It is far easier to slow this down that to pull it back in once it is widely available.

I think it is pretty clear that this will adversely affect folks using stack traces for performance profiling, etc.

Given these issue lack factual support, I and the rest of the members of the Apple JSC team cannot support the suggested STC changes. I believe that are efforts should be focused on communicating the benefits of PTCs, how to use them, and the minor changes web developers will notice.

— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/tc39/ecma262/issues/535#issuecomment-216710348

littledan commented 8 years ago

Performance concerns of PTC

I believe it would be reasonable for TC39 to consider implementation constraints. PTC was specified before the current staging process took effect, so we didn’t get the two-implementation feedback that we would’ve gotten in the current process.

Now that we have this feedback from Microsoft, it might be reasonable for the committee to consider the diversity of reasonable implementation choices. Chakra chooses to use the Windows ABI, which comes with debugging integration with the rest of the platform. Something that’s implementable in one engine might not be practical in another. That’s part of the wisdom of the current requirement of two implementations for a new proposal to get to Stage 4.

STC would solve a performance problem by making tail calls pay-as-you-go, rather than a big all-in-one regression. Developers would likely only use STC where it provided the most value--in tail-call driven loops--rather than all over the place, as PTC is. If an engine does have nontrivial overhead for tail calls, then STC would make a regression on existing code, and PTC would not.

I believe at this point all browsers implementing ES2015 have put in a significant amount of work doing optimizations to make up for the performance challenges of the new language standard. But there are more concerns with this than just performance.

Because PTC elide stack frames, debugging would be more difficult.

You mentioned two approaches here, keeping a side stack when debugging, and turning off PTC when developer tools are open. Both have significant disadvantages.

Side stack: With the ShadowChicken approach, the stack trace may change based on details of garbage collection. When the garbage collector runs, it can remove frames which end in a tail call. These then won’t show up on a stack trace. This will remove valuable debugging information in some cases, when many JS engines work hard to maintain correct information.

Turning off PTC: Globally turning off PTC is a pretty heavy switch. It is a cross-cutting decision that will affect any nested library that the developer may not be aware of. If turning off PTC is a widely used feature of development tools, it reduces the value of the PTC feature itself, as libraries, application frameworks and users would avoid depending on the feature in order to continue functioning in this mode.

PTC will eliminate stack frame from telemetry data.

Many users have spoken up about how this telemetry data is important to them, including @davepacheco of Joyent, @jeffmo of Facebook, and various groups within Google (bug thread).

You cite the fact that Safari has shipped tech previous with PTC and not received bug reports about the missing stack trace. However, this issue wouldn’t result in a website simply doesn’t load. Instead, it will be that website authors will have more trouble debugging issues that come up on browsers that ship PTC. This means that users of browsers with PTC could suffer from worse user experience due to issues not being properly diagnosed and resolved, compared to users of browser which don’t have PTC.

However, currently, with a rather small population, major website owners are probably simply not seeing many of these shortened stack traces yet. They are likely able to do effective debugging for most of their current population, and might not even see any frequent enough reports from the small PTC population to draw concern.

getify commented 8 years ago

Developers would likely only use STC where it provided the most value--in tail-call driven loops--rather than all over the place, as PTC is.

I'm not sure this fits with how other evolutions of the language and "best practice" have played out. "Most" developers don't understand nuance/context in "best practice". "Most" developers actually prefer for "x is the new y" type best practices. For example, see "let is the new var", "arrow is the new function", etc.

Even if "let" or "arrow" was shown to create some performance problems in some engine, the overwhelming majority of developers would be using them across the board. Even for those of us who make our living in teaching JS to developers, I can't imagine there being widespread effective education around the nuances of when you'll want to use tail calls for memory and when you'll want to avoid tail calls for performance. I have intuitions about that stuff, but the majority of the JS population won't, and won't care. They'll follow whatever conventions are common for their framework of choice.

I can imagine certain language patterns (especially those which come from other languages transpiling to JS) taking a strong hold in this future "JS has tail calls" world, whenever it arrives, like FP, obviously. As that happens, we'll see a high percentage, even a majority, of calls being tail calls. It's entirely plausible we'll see "tail call is the new end-of-path function call" for that section of the JS ecosystem.

There's already several things that "FP in JS" devs do which are not strictly "fastest" or "most memory efficient", like for example filter(), map() and reduce() returning new arrays each time, or how bind() (for currying, not this) has been historically quite slow, though it's recently finally been improved. Those performance problems haven't stopped those techniques from being used. Tail calls being "slower" in an engine, in exchange for constant stack, isn't going to deter its usage either.

There's a likely future with a pretty heavy amount of tail calls happening in JS programs, and engines are going to have to do whatever they can in that future to try to optimize. I think it's a mistake to analyze now, in a world where no one can do tail calls in JS, that there's relatively few tail calls, and project that this will continue to be the case. It probably will change.

getify commented 8 years ago

As a side note, this likely future where tail calls are used heavily and not just as some niche technique, is part of my objection to the call-site syntax approach (as opposed to the function level). The syntax literally becomes a tax on this coding pattern, one that's paid more heavily the more you want to take advantage of the pattern.

I can already see the future medium blog posts that criticize JS for adding syntactic cruft to otherwise "elegant tail call coding/recursion patterns". Someone will dig up this GH repo history and mumble something in the comments about it being because of old archaic engine limitations, and everyone will roll their eyes and mutter under the breath, "silly ol' ugly JS again".

getify commented 8 years ago

If turning off PTC is a widely used feature of development tools

I've been assuming that the devtools story around tail call debugging would end up looking similar to how chrome devtools has the "script blackboxing" that makes a script opaque from the debugging perspective. I don't understand/believe the assertions that tail calls can't be made to work sensibly (even configurable) in devtools mode.

saambarati commented 8 years ago

@littledan said:

Side stack: With the ShadowChicken approach, the stack trace may change based on details of garbage collection. When the garbage collector runs, it can remove frames which end in a tail call. These then won’t show up on a stack trace. This will remove valuable debugging information in some cases, when many JS engines work hard to maintain correct information.

I don't think this is a concern for real debugging experiences. It's sufficiently rare to have an interactive debugging experience/session where you care about more than the top 5 frames. I know when I'm using a debugger, 99% of the time all I care about is the top most frame.

getify commented 8 years ago

It's sufficiently rare to have an interactive debugging experience/session where you care about more than the top 5 frames.

Just articulated a compromise to many of the concerns here versus the motivations behind STC: "adaptive" tail-calling; that tail-calls only beyond a certain fixed limit: https://github.com/tc39/proposal-ptc-syntax/issues/16

littledan commented 8 years ago

@getify Could you tell me more about how you'd solve the devtools/error.stack issues?

getify commented 8 years ago

@littledan

the concern about turning off PTC while devtools/debugging and having that negatively impact (break) other libraries/frameworks that expect it to be in force, could be mitigated in the same way we deal with not wanting the noise of step-debugging in blackboxed scripts... meaning that PTC would continue to happen inside of any blackboxed scripts, even if devtools was open.

for the error.stack thing, the link I just posted is one potential compromise idea.

getify commented 8 years ago

I think it's also important to note that given there's no current JS support for these unlimited length call stacks, none of the error.stack telemetry stuff is handling any of that currently, so we wouldn't necessarily be taking that away in a breaking way. It's possible their infrastructure couldn't even handle if they all of a sudden received a stack that was hundreds of thousands of frames long.

One could imagine in the future the desire to have, for error.stack/telemetry purposes, a stack that was, for example, only the first 20 frames, the last 20 frames, and everything in between thrown away.

msaboff commented 8 years ago

Since much of this discussion is based on assumptions with very little data, I decided to instrument JSC and see what happens on real webpages. I added compile time and runtime counters, basically to determine what percent of calls end up as PTC at compile time and then determine what percent of actual calls are PTC versus normal calls. The runtime result fit my gut instinct at ~5%. The percent of calls that get compiled to PTC surprised me on the low side, typically much less than 1%. I did not include true constructor calls in the data.

I performed 2 broad tests, first loading the top ~1000 Alexa sites, and then navigating specific sites that are high on the Alexa list that I could access without an account or with my own account. First the Alexa data, with raw numbers:

At compile time there were 6,683,375 calls of which 24,393 where PTCs (0.36%). While executing, 179,610,729 calls were made of which 8,396,396 where PTCs (4.67%).

Here is summary data for the specific sites:

site compiled PTCs actual PTCs
google.com 0.24% 4.14%
facebook.com 2.14% 4.93%
youtube.com 0.06% 0.90%.
wikipedia.org 0.90% 3.54%
amazon.com 0.12% 1.39%
ebay.com 0.19% 6.08%
paypal.com 0.66% 0.19%
apple.com 0.20% 3.30%

For sake of discussion, let's summarize this data with about .4% of calls in javascript source in the wild will be PTC's. At runtime, these paths seem to be more prominent and account for ~5% of actual paths. There are several things this data says to me:

  1. Very little code, less than .4%, are PTCs.
  2. PTCs occur along paths that are somewhat common as those paths are 10x likely to get executed.
  3. Given items 1. and 2., it would seem to me that PTCs exist in some common javascript libraries. If this is true, then the debugging and telemetry issue are much more localized.
  4. Performance concerns are not as much of an issue given that only 5% of actual calls are PTCs. If a browser's PTC implementation is 10% slower that a standard call, that will only have a .5% impact overall on the typical webpage.
  5. The STC tax discussed earlier appears to have a higher price in that we will require programmers to opt in to tail calls to mitigate an issue in .4% of the current source. Seems like a high price to pay for an arguable small problem. Even considering the facebook.com data, the source prevalence of PTC's for that site is ~2%.
littledan commented 8 years ago

@msaboff Thanks for collecting this data. Interesting to see that less frequently called code does fewer tail calls.

4+% of runtime calls sounds like plenty of calls to me, and I would be concerned about losing nearly 1 in 20 stack frames for debugging. This seems to confirm my fears.

littledan commented 8 years ago

About point 5., I don't think any of the existing calls should be changed to make a tail call. Instead, STC should be used in new code which wants to take advantage of a feature which was not previously present at all, which is to allow tail-recursive loops without very low restrictions on the number of the number of iterations. STC and PTC are not performance features; it is important that we document this for users.

msaboff commented 8 years ago

About point 5., I don't think any of the existing calls should be changed to make a tail call. Instead, STC should be used in new code which wants to take advantage of a feature which was not previously present at all, which is to allow tail-recursive loops without very low restrictions on the number of the number of iterations. STC and PTC are not performance features; it is important that we document this for users.

My point is that because there are concerns that PTCs will cause issues, it is suggested that we introduce STCs. I have no expectation that programmers will go back and add the appropriate syntax for STC at any of the current PTC sites. In fact given that they'll most likely want their code to run on older browsers, I expect that number to be close to 0. If we abandon PTC in favor of STC though, every programmer that wants to use tail calls must add whatever syntax STC requires specifically because we are worried about the .4% of PTCs in source code today. That is a high future tax for an arguable a small problem today.

jeffmo commented 8 years ago

I have no expectation that programmers will go back and add the appropriate syntax for STC at any of the current PTC sites

I think this is a source of the disagreement: The notion that any calls that exist in tail position today will necessarily benefit from being picked up as a PTC tomorrow.

I suspect proponents of the STC proposal would argue that, because no code today needs PTC, there is little benefit (and clearly a change in semantics) to retroactively instituting it for that code. Moreover, the basis of the STC proposal seems to be that tail calls are only meant to be intentional and shouldn't be accidental -- thus retrofitting the behavior into existing code is really a non-goal.

msaboff commented 8 years ago

After discussion with colleagues and some reflection, I'd like to add a couple more points that I believe are relevant to this thread.

  1. Given the data says there is a 10x likelihood that a current PTC gets called compared to a standard call, we suspect that the majority of those PTCs are commonly called wrapper or forwarding functions.
  2. Typically telemetry systems match the top one to few stack frames, and only rarely if ever depend on the rest of the stack for anything, since a bug in a function can be triggered by multiple different call sites. Are there any known examples of telemetry systems that depend on matching the whole call stack?
  3. Telemetry systems already bucket by browser since some errors are particular to specific browsers, some expected API invocations differ between browsers, and error.stack formatting differs across browsers. So, it doesn’t matter if new browsers elide frames that old browsers included — telemetry systems will be self-consistent within browsers versions as they always have needed to be.

Taken together the data would suggest that the impact of current in the wild PTCs on telemetry systems would be quite low.

concavelenz commented 8 years ago

RE: 1. PTC doesn't dictate that the method is trivial.

A method could easily end with trivial check and the interesting method would be lost.

return ThrowIfNullOrUndefined(result);

RE: 2. The interesting data can be arbitrarily deep. Often we see 4 or 5 frames of logging infrastructure.

RE: 3. That different browser report different frames isn't particularly important, but that interesting frames are lost is the issue. PTC requires that you "get lucky" to preserve the correct stack frames.

msaboff commented 8 years ago

RE: 1. PTC doesn't dictate that the method is trivial.

Of course. I wrote that I speculated that the PTC calling methods are wrappers.

RE: 3. That different browser report different frames isn't particularly important, but that interesting frames are lost is the issue. PTC requires that you "get lucky" to preserve the correct stack frames.

Since the frames lost are PTC callers, they don't contain the reporting code, but are on the path to the reporting code. IF they are wrapper functions, losing those frames from the stack trace is a very small lose of information.

concavelenz commented 8 years ago

I meant to ask, if you are only count "strict" code where PTC is active or if you are counting non-strict code.

On Fri, May 6, 2016 at 3:26 PM, Michael Saboff notifications@github.com wrote:

RE: 1. PTC doesn't dictate that the method is trivial.

Of course. I wrote that I speculated that the PTC calling methods are wrappers.

Right, and I'm stating I don't believe there is any reason to believe that.

RE: 3. That different browser report different frames isn't particularly important, but that interesting frames are lost is the issue. PTC requires that you "get lucky" to preserve the correct stack frames.

Since the frames lost are PTC callers, they don't contain the reporting code, but are on the path to the reporting code. IF they are wrapper functions, losing those frames from the stack trace is a very small lose of information.

I think we are using different meanings of "reporting". Here I mean the path to the function that is causing the stack trace to be collected, which may not be source of the "error" but just some bit of the logging infrastructure:

function report(msg) { var record = createReportRecord(msg); // add a stack trace sendReport(record); }

function doStuff() { ... if (...) { ... return result; } report("failed") }

— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/tc39/ecma262/issues/535#issuecomment-217578582

msaboff commented 8 years ago

I meant to ask, if you are only count "strict" code where PTC is active or if you are counting non-strict code.

I counted all code. I wanted to measure how common PTC are across all web pages.

As far as likelihood of a PTC being elided from a stack trace that gets reported via telemetry, you and I are both speculating. Maybe we can agree that ~5% of frames will not appear in the stack trace. As far as what those elided functions look like, we don't know without further investigation.

bterlson commented 8 years ago

@msaboff Do you know what percent of calls are syntactically tail calls but not only due to containing code being sloppy?

msaboff commented 8 years ago

Do you know what percent of calls are syntactically tail calls but not only due to containing code being sloppy?

I do not. I put my compile counters at the point where the parser is issuing a call byte code, which is after where we've determined whether or not it is a PTC and for your question WHY we determined it is or isn't a PTC.

littledan commented 8 years ago

Seems like we might expect this number to increase a bit over time as strict mode usage increases.

bterlson commented 8 years ago

Either a bit or a lot, maybe, depending on how prevalent strict mode is today. I haven't seen numbers for a couple years, but last I checked sloppy was by far the most prevalent mode.

claudepache commented 8 years ago

There is a fear that useful debugging info (stack trace report) would be degraded with PTC. The fear is valid, but pushing back PTC as possible remedy looks backwards. Since the issue is with stack trace, not with PTC per se, stack trace should be improved rather than PTC degraded.

For example, I imagine that a given stack frame could keep track, for debugging purpose, not only of the function that is currently using the frame, but also of the first function that used it (the one whose call induced the creation of the frame), and of the number of PTC that has occurred for that frame. Then, experience would show if more debugging info is needed.

rossberg commented 8 years ago

@claudepache, TCE doesn't mean reusing stack frames, if that's what you're thinking. That wouldn't be possible in most cases, because functions differ in their number of arguments, of local variables, calling conventions, or even general stack frame layouts, e.g. in the presence of different optimisation tiers. You can even tail-call functions that are not JavaScript at all, and vice versa.

What a tail call generally does is removing the current stack frame before it performs an ordinary call. The reuse you imagine is a possible optimisation in only a few special cases (mostly self recursion).

bterlson commented 8 years ago

I found the data I alluded to above (see my presentation): http://wiki.ecmascript.org/doku.php?id=meetings:meeting_jan_29_2013. In early 2013 I found that only ~4% of sites contained some strict code. I suspect this has increased lately but I'm not sure how much.

@msaboff it would be really helpful to know what percent of strict calls are also tail calls if you can find out somehow :)

saambarati commented 8 years ago

@claudepache @rossberg-chromium Regardless of the mechanism used to accomplish this, there are methods for keeping around the interesting bits of the stack in a world with tail calls. I.e, keeping the top X frames when the debugger is enabled. This will solve almost every interesting debugging scenario in a world with tail calls: -- Your program is not written with PTC in mind and is not knowingly using PTC. This means that in any given stack trace, there will be very few frames that would have been elided because of tail calls. This means that smart debugger integration will show all those frames that would have been elided because it will almost always be less than X. -- Your program is written to take advantage of PTC and will have many loops written using recursion. In such a scenario, the debugger will show you the top X tail called frames (along with the rest of the not tail-call elided frames). This is almost always a good solution because the most interesting frame is the top frame. All other X - 1 frames are just nice to have. And sometimes, having these frames will just cause clutter. It's not obvious that a program written to utilize PTC that has a loop written as recursion will always want to see X iterations of the loop in the call stack. It may even be beneficial to have the debugger intelligently compact frames that are obviously part of a tail recursive loop.

ljharb commented 8 years ago

@saambarati what about the cases where one wants to introspect the stack frames at runtime, sans any debugger being present?

msaboff commented 8 years ago

it would be really helpful to know what percent of strict calls are also tail calls if you can find out somehow :)

@bterlson, I found the time to collect some "strict mode call" numbers. I added another counter at compile time. If we generate a "call" byte code and we are currently in strict mode, I bump the new counter. Again I ran the top 1100 Alexa sites and collected the data. I then went to a smaller set of individual pages, navigated around them and collected data for each of those sites.

For the Alexa tests, at compile time there were 7,703778 calls of which 28,906 were PTCs (0.38%). 716,931 of all calls where found in strict code (9.31% of all calls are strict and 4.03% of strict calls are PTC). While executing, 161,758,015 calls were made of which 8,194,931 where PTCs (5.07%).

For the individual sites I navigated, my process was less scientific. I didn't record how I navigated around each site the first time, so I likely hit different paths this time. Here is summary data for the specific sites:

site compiled PTCs compiled strict strict that are PTC actual PTCs
google.com 0.13% 9.31% 6.58% 3.91%
facebook.com 2.01% 41.07% 4.90% 4.86%
ebay.com 0.21% 6.59% 3.13% 4.93%
paypal.com 0.53% 19.93% 2.68% 2.46%
apple.com 0.26% 6.18% 4.25% 3.90%

Facebook and PayPal for the win on using strict mode!

It is interesting to note that in most cases the incidence of PTC in strict source is fairly close to the percent of actually called PTC. For the Alexa run, 4% of compiled strict calls are PTC while at run time 5% of calls are PTC.

concavelenz commented 8 years ago

Thanks for getting some numbers regarding strict mode. But I'm not really sure what conclusions I can draw from these updated numbers.

As an FYI, plus.google.com (not classic) and photos.google.com should be almost entirely strict. In contrast, I'm surprised google.com has any strict code.

hax commented 7 years ago

Any conclusion?

YurySolovyov commented 6 years ago

With Firefox launches like Electrolysis and Quantum, are there any changes with respect to Tail Calls ? Asking because I remember sandbox or process model as the reason FF can't have Tail Calls

hashseed commented 6 years ago

iiuc none of these affect SpiderMonkey, which iirc requires stack frames for security checks.

enright commented 6 years ago

I write functional ES6 code that depends on proper tail call implementation according to the spec. Use my code in Node v6 with harmony flag...works fine. Use my code in Node v8...blows out the stack. This is stupid.

Why in the world should I have to put up with non-conformance? Are you kidding me?

glathoud commented 6 years ago

Here is one possibility for developers to use fast tail calls today, without needing extra keywords in JavaScript: https://github.com/glathoud/fext

glathoud commented 6 years ago

I do not see as spam the fact that one can generate optimized code using today's standard JavaScript.

leobalter commented 6 years ago

@glathoud I don't know what flagged this as spam but this library - despite being interesting probably useful - is not on topic for what should be discussed here. The topic holds concerns for implementing Tail Calls natively as in the language specs, answering to another topic proposing a syntactical Tail Call grammar.

glathoud commented 6 years ago

@leobalter At first sight, yes, off-topic.

But whoever tries to implement the whole complexity of mutual tail recursion optimization in a dynamic language - in a broad sense - like JavaScript might well stumble upon a few issues. My 2 cents :)