yield syntax
Brendan Eich wrote:
On May 17, 2009, at 12:43 PM, Mark S. Miller wrote:
On Sun, May 17, 2009 at 11:00 AM, Brendan Eich <brendan at mozilla.com> wrote:
Analogous to direct vs. indirect eval in ES5 (15.1.2.1.1), there is no purely syntactic specification for what Neil proposes. A runtime check is required. So I don't see why you are focusing only on syntax here.
I don't follow. What runtime check? For the eval operator, the runtime check is whether the value of the eval variable is the original global eval function. It makes no sense to have a corresponding global yield function value.
If we reserve yield then you're right. One of the appealing (at least to me) aspects of Neil's suggestion was that it would avoid opt-in versioning required by reserving yield (which is used in extant web content, or was when we tried reserving it without opt-in versioning -- the particular use was as a formal parameter name, used as a flag not a function).
Oh, right. We've been talking at cross-purposes. I assumed that you were suggesting that 'yield' should be contextually reserved. That is what I've been saying couldn't work due to ambiguities.
On May 17, 2009, at 5:05 PM, David-Sarah Hopwood wrote:
Brendan Eich wrote:
On May 17, 2009, at 12:43 PM, Mark S. Miller wrote:
On Sun, May 17, 2009 at 11:00 AM, Brendan Eich <brendan at mozilla.com> wrote:
Analogous to direct vs. indirect eval in ES5 (15.1.2.1.1), there
is no purely syntactic specification for what Neil proposes. A runtime check is required. So I don't see why you are focusing only on
syntax here.I don't follow. What runtime check? For the eval operator, the
runtime check is whether the value of the eval variable is the original
global eval function. It makes no sense to have a corresponding global
yield function value.If we reserve yield then you're right. One of the appealing (at
least to me) aspects of Neil's suggestion was that it would avoid opt-in versioning required by reserving yield (which is used in extant web content, or was when we tried reserving it without opt-in
versioning -- the particular use was as a formal parameter name, used as a flag
not a function).Oh, right. We've been talking at cross-purposes. I assumed that you
were suggesting that 'yield' should be contextually reserved.
Oh, I should have seen that. Thanks for clarifying.
That is what I've been saying couldn't work due to ambiguities.
RIght, no sane way to reserve yield or another keyword contextually
(e.g., only in callee position; "contextually" in the sense of get and
set in object initialisers).
In JS1.7+, we reserve yield with opt-in version selection via <script
type="...;version=1.7"> and the like. In such a script, yield and let
are reserved the same way the other identifiers are, which is how ES5
does it (with an extension we've discussed -- the extension where a
reserved identifier can be used after 'function' -- but that's not
interesting for this discussion).
Brendan Eich wrote:
On May 17, 2009, at 6:39 PM, Neil Mix wrote:
- we could always allow parenthesis to be dropped when the yield is the entire expression of an expression statement or the right-hand side of an assignment.
Right-hand side of assignment is ok without parens in Python because assignment is a statement.
In JS if you allow assignment expressions ending in unparenthesized yields, then you can have unparenthesized yields in argument and initialiser lists, comma expressions, and in the middle and final operand positions in ternary (?:) expressions.
- in my experience with JS 1.7 I almost always had to parenthesize the yield expression when it was in some other kind of expression. An in the cases where parenthesis weren't required, I parenthesized anyway to avoid ambiguity and maintain coding style consistency. (And because I got tired of predicting incorrectly whether or not parens would be required in a particular context.)
The only contexts we allow you not to parenthesize in JS1.7 are assignment expressions and final argument in list. But see above -- the assignment expression loophole is big enough to allow
foo(a = yield b, c);
One argument, or two?
If it is allowed, then it should be two. It would be very surprising if
foo(a = b, c);
had two arguments (as it does), but the above expression with yield had one.
But I agree that it may be better not to allow it.
So I would argue that there are two syntactical forms of yield, yield E and (yield E), and that the rules regarding the requirement for parenthesis are hard to predict (from personal experience). Therefore, I argue that it would make sense to simplify a bit:
- the yield E form may be used when it is the entire expression of an expression statement
- all other times it must be parenthesized
Agreed; this closes the assignment expression loophole.
This would disallow
foo(yield x);
which seems unnecessary.
Igor Bukanov wrote:
2009/5/18 Brendan Eich <brendan at mozilla.com>:
On May 18, 2009, at 2:25 AM, Igor Bukanov wrote:
The plus side of this is that an empty generator can be created with a straightforward:
Generator(function() {})
and not with a rather unnatural
(function() { if (false) yield; })() No one makes empty generators.
For me the problem with the way the generators are defined is that a dead code like that "if (0) yield;" affects the semantic by mere presence of it. Surely, this is not the first feature in ES that has that property - "if (0) var a;" is another example. But "if (0) yield;" sets a new record affecting the nature of the whole function.
A more explicit alternative is to require some kind of decoration on the function definition, e.g. (just a straw man):
function generator foo() { ... }
On May 19, 2009, at 2:00 PM, David-Sarah Hopwood wrote:
If it is allowed, then it should be two. It would be very
surprising iffoo(a = b, c);
had two arguments (as it does), but the above expression with yield had one.
But I agree that it may be better not to allow it.
Good; it was a loophole in JS1.7 and up, which I think should be closed.
Agreed; this closes the assignment expression loophole.
This would disallow
foo(yield x);
which seems unnecessary.
I thought so too, but Python keeps its grammar simple this way, and
simpler is better, ceteris paribus. JS1.7 allows this but also suffers
the assignment loophole. Getting rid of the latter but not the former
is grammar-hacking busy work. I haven't done it in the WebKit/
JavaScriptCore/parser/Grammar.y.
On May 19, 2009, at 2:08 PM, David-Sarah Hopwood wrote:
A more explicit alternative is to require some kind of decoration on
the function definition, e.g. (just a straw man):function generator foo() { ... }
Or just (we discussed this briefly last summer in Oslo):
generator foo() { ... }
In JS1.7, we followed Python, which uses def, same as for defining a
function, and distinguishes generators by presence of yield (which
makes value returns illegal). It seems unwise to diverge from Python
without more evidence of an actual usability problem. We have no such
evidence from shipping JS1.7 and up in Firefox 2 and up.
Brendan Eich wrote:
Maybe I misunderstood what Neil was proposing, but I took him to mean something equivalent to:
This is a purely syntactic specification.
It does have some quirks: if you define a function called 'yield' and try to call it, then the call will still be treated as a yield-expression rather than a function call. For that reason I'm going cold on the idea. I would prefer something entirely unambiguous, like
@yield expr
or an opt-in that makes 'yield' (and 'let') reserved everywhere. Either would make parentheses around a yield expression unnecessary, or at least not necessary in as many cases. ('yield' would behave syntactically like 'typeof', perhaps with lower precedence.)
[...]
Python does not attempt to contextually reserve 'yield'. It uses an opt-in that makes 'yield' a keyword that is reserved everywhere. From PEP 255:
A new statement is introduced:
yield_stmt: "yield" expression_list
"yield" is a new keyword, so a future statement[8] is needed to phase
this in: in the initial release, a module desiring to use generators
must include the line
from future import generators
near the top (see PEP 236[8]) for details). Modules using the
identifier "yield" without a future statement will trigger warnings.
In the following release, yield will be a language keyword and the
future statement will no longer be needed.
That would be ambiguous, if (hypothetically) 'yield' were not a keyword in Python. For example
(yield (foo))
would match both a parenthesised <yield_expr>, and a parenthesised <NAME> 'yield' followed by a trailer giving function arguments. (This involves the <power>, <trailer>, <arglist> and <argument>
productions that weren't found by your grep.)
Similarly for (yield - 1) or (yield + 1) or (yield /re/g) in Harmony. If these are treated as being yield-expressions, then that is not compatible with any ES3/5 code using 'yield' as a variable name, but if they are treated as having their ES5 meaning, then there are a bunch of ugly special cases for what token can validly begin the expression after 'yield'.
Unambiguous grammar first, please.