-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Syntax Strawmen #1
Comments
I would hope that any syntax we arrive at will make it a syntax error for opting into tail calls and subsequently writing code that involves calls not in tail position. The exact mechanism for this is yet to be determined but it seems possible and desirable in my opinion. |
FWIW I ran a Twitter poll on a few syntaxes, and |
First, I don't want any syntax. But operating for the moment on the assumption that it may end up having to happen, let me elaborate: I am imagining some sort of "decoration" on the syntax of a function (and potentially also a generator) declaration that indicates all PTCs from inside that function should be optimized as tail calls. For example: #function foo() { .. }
#function *bar() { .. }
let x = #function *baz() { .. }
x = {
#bam() { .. },
#["qux"]() { .. }
}; I originally liked the Others suggested words similar to rec function foo() { .. }
tail function foo() { .. }
function rec foo() { .. }
function tail foo() { .. } I can't really imagine practical use-cases for wanting PTC forms in an |
But syntax bikeshedding aside, my main reasoning for wanting to use a function-level syntax instead of a call-site syntax is the idea that call-site syntax creates a footgun if I annotate one of my PTCs but forget to annotate another intended one. For the record, in my recursive coding, it's quite common that I have two or more PTCs in a function. That's especially true when you consider the various rewritten forms (with inner funcs, etc) that you have to do to turn a non-PTC recursive call into a PTC. In general, I think there will always be more (and maybe many more) PTCs than function declarations. But if we just went with the assumption of an average of 2 PTCs per function, then I'd really prefer to have half as many places to remember to add explicit syntax. What if I mark a function as PTC-desired, but there's a function call in there that I don't want to be optimized as a tail call? The "opt-out" is very easy. You just simply do anything that makes it not a PTC. There's several ways to imagine doing that fairly unobtrusively, such as My biggest "footgun" concern is thinking about a pattern for a self-adjusting form of recursive coding that I've advocated, which uses Example: "use strict";
function foo(x) {
function _foo() {
if (x > 1) {
acc = acc + (x / 2);
x = x - 1;
return _foo();
}
}
var acc = 1;
while (x > 1) {
try {
_foo();
}
catch (err) { }
}
return acc;
}
foo( 123456 ); // 3810376848.5 This approach is the best I've conceived for writing recursive code right now during the bridge from non-PTC engines to PTC engines. It allows code to "work" everywhere, but get better and faster as newer engines come about (aka progressive enhancement). The footgun comes in that if I have to do explicit syntax on each PTC, and I get a few of them but forget one, this adaptive approach will mask that problem. I'd only be able to "catch" the mistake indirectly through profiling. It's much easier to catch (especially with linting rules, for example) that I missed the syntax opt-in on a function declaration than on PTCs sprinkled throughout a function's code. |
@getify There is no spec which says that a stack overflow will throw any particular kind of recoverable error, as opposed to crashing the page (as out-of-memory conditions do on many platforms), or allocating all of the memory in the system for the stack which could cause another OOM at any time. Therefore, in a pre-PTC world (e.g., the world of stable browsers), this pattern would not be guaranteed to work per-spec. This pattern as written also seems dangerous for catching all errors, which could cause issues if an unexpected error is thrown. Further, because the extra driving while loop is needed, the terminating condition needs to be written in two places, the function doesn't have the flexibility to tail-call into other functions reliably (without an additional tracking variable), and |
Would it be possible to accomplish the explicit syntax opt-in via existing valid syntax rather than new syntax? For example, in the vein of how ASM.js took existing (but uncommon) syntactic patterns as annotations of desired optimizations, I'm imagining something like: function foo(x) {
if (x < 1) return x;
{ return foo(x-1); } // <-- explicit block containing only the PTC `return` statement.
}
function foo(x) {
if (x < 1) return x;
return foo, foo(x-1); // <-- comma separated list where first element is the PTC function, second is the PTC call
}
// etc |
|
@ljharb my intent is to do exactly that, just like ASM.js did. I'm likening the "optimization" of PTC to the compilation optimizations that ASM did, by inferring that expressions with | 0 are intended to be ints, or whatever. |
aha, interesting - makes sense. wouldn't that run the (tiny) risk of opting a non-tail-call function into PTC? Surely somebody somewhere is using that syntax. In the case of asm.js, "use asm" is the thing that enables those optimizations, so there's no compat hazard. |
I don't see the value of either of the subset proposals over an explicit syntax personally. The syntax is also highly confusing (can I remove this useless block? Hmm....) and difficult to read. |
Yes, but that only means that this function call could run slower than it used to (which btw is already possible... engines regress performance from time to time). If you discovered the performance regression impacting your code, all you'd need to do to opt-out is slightly alter your pattern to avoid the specific narrow PTC pattern. Whatever it would be, would need to be a narrow enough slice as to be fairly unlikely to hit much code. |
I'm coming to like return continue despite its verbosity. One key reason is arrow functions: an ergonomic usage for arrow functions with concise bodies seems necessary. |
I didn't argue the "why" behind this in this thread, because there's another thread I started to explain why I'm objecting to the new syntax in the first place. TLDR: I think new syntax makes the migration/adoption story for tail call based coding much less friendly. The advantage of using existing syntax is that it's easier to bridge to it (see my several other references to adaptive recursion meta programming). |
@getify thanks for splitting off the 'why' into a separate thread; let's leave this to discuss syntax ideas. We have current draft spec text using One piece of open syntactic space is On the topic of these options, @erights mentioned to me that it would be not so great if we ended up with something excessively verbose, e.g., Whatever the alternative is, in my opinion it would be great if we can preserve the expression (rather than statement) syntax that we have now. |
Definitely against anything too verbose. Back when I think it was still reserved, I suggested "goto" for this purpose, but too late now. The difficult of searching for this use of I like the idea of using |
We could also consider |
After just a few minutes, |
@erights, I also like return in.tail foo();
return.tail foo();
something else? |
If it's going to be "return.tail" then how will we write a tail call inside complex expression like |
With only |
@bterlson If I understand you correctly, sounds like |
@littledan that is what I'm suggesting, yeah. Wonder if this aligns with what @erights is thinking? |
I like the .tail idea. And the combination |
I want to understand what is being proposed for: function f() { With "in.tail", am I correct in understanding this would be: function f() { I don't know what this looks like with only "return.tail". I wish we could function f() { or in the alternate proposal: function f() { I'm not really clear on where the real value is of not returning the result On Mon, May 9, 2016 at 2:36 PM, ishell notifications@github.com wrote:
|
The idea is the following:
|
The
Syntax error:
A shortcut for |
Okay, I gonna say it: |
@rossberg-chromium can you present an alternative? |
@littledan, the current proposal is a perfectly sensible alternative AFAIAC. I don't buy the searchability complaint. Lots of other syntax is far less searchable, so how is that a requirement all of a sudden? |
That's a very good point that had not occurred to me. I agree that it is a good design rule to impose in general for the use of |
The problem is not with new syntax in general, it's with repurposing existing syntax, especially for power-user features that most users won't know about a priori. What I'd like to optimize for here is:
Because
Because this feature is relatively niche, it also may not come to quickly dominate these searches. TLDR: I prefer terminology that is clearly identifiable to users the first time they use it, and likely to eventually dominate searches for that term. |
For what it's worth, I think we'll generally make more progress here by accepting (but asking for details and clarification) the constraints other people have, rather than dismissing them. The only plausible result of dismissing constraints that other people have (that they feel strongly about) is deadlock. Let's start by assuming we can find a solution within the constraints, and only if we've fully explored the space and can't find any, then we can start pushing on people to defend their strongly-held constraints more aggressively. |
As a precedent, I recall that the
There are also already many results for |
I think "return continue" will become more searchable as the language feature comes into existence and use. Although there are results already, search engines can use heuristics like downranking for intervening punctuation. GitHub search already fails to get good results for my queries which I think are reasonable. |
@wycats, generators have repurposed |
How does |
|
Ahh, of course! Thanks. |
@littledan My understanding is that do-expressions must always have I like it the best out of all the proposals I've seen:
So my vote goes toward |
Because of gzip, I don't think the length of the keyword will have any meaningful impact. The other points stand, ofc. |
@ljharb Not all JavaScript code is gzipped: Chrome/Firefox extensions, Node.js, phone apps, embedded devices (IoT), etc. |
@Pauan filesize isn't as much of a concern in those arenas; but either way, I would object strenuously to any proposal that used "smaller filesize" as its argument, as I feel that the existence of that argument weakens every other argument used alongside it. Optimizing for keyword length (for filesize; as opposed to for readability) will just lead to illegible hard-to-maintain code, and I wouldn't want that argument given even implied approval. |
@ljharb Sorry, guess I shouldn't have said anything then. |
@Pauan not true, thank you for taking the time to share your thoughts and keep them coming. It's very helpful for us! But this proposal is dead and PTC continues to be the consensus position. It's interesting to think about what could have been, though :) |
@Pauan I didn't mean to give you that impression; I like the rest of your points :-) |
I see no reason why |
I know, I just think that now at 2018 when |
Why not *implement it now and use a directive like That way people that know what tco is and dont want to do a huge refactor to their code to remove all the treampolines when this FINALLY lands can stop waiting for people to flame each other and get on with their lives????
|
One big question is what the syntax is. I think there are two primary proposals floating around, both modifications of the
return
keyword - statements starting with:(Note that return! doesn't work due to conflicting with existing negation syntax)
One concern with a statement form is that arrow function concise bodies couldn't contain a tail call. That could argue for an expression form.
Another issue is the ternary operator, though my sense is that syntactic opt-in at the statement level works here too -
!return x ? Y : Z
could only allow calls in tail position in the expressions Y and Z.The text was updated successfully, but these errors were encountered: