some generator issues
On Apr 22, 2007, at 1:12 AM, Yuh-Ruey Chen wrote:
There are a couple of issues I see with the generator proposal.
- What happens when send(x) is called on a newly created
generator? In Python, this results in an exception unless x is None.
See developer.mozilla.org/es4/proposals iterators_and_generators.html#generators which contains this sentence:
"A generator must be started by a method call to next() or send
(undefined). It is a TypeError to send a value other than undefined
to a newborn generator."
- What happens when a generator calls next/send on itself e.g.
var $gen; function foo() { $gen.next(); // what should this do? yield; } $gen = foo(); $gen.next();
In Python, this results in an exception.
This needs to be specified; it's implemented that way in JS1.7 in
Firefox 2. Thanks for pointing out the spec gap.
- Is there any way to get a generator from within a generator
function? One use case for this would be asynchronous callbacks (can't
synchronous because of issue 2 noted above):// client calls this function somewhere function clientFoo() { let serverResponse = yield (server.asyncProcedure (clientFooGenerator)); print(serverResponse); }
// server.asyncProcedure calls this once it's finished, passing clientFooGenerator for gen function serverCallback(gen, retvals) { gen.send(retvals); }
The only way to do this right now is to wrap the generator function within a function that introduces the iterator to the generator function's scope:
function foo() { var $gen = function() { // generator code that can use $gen }(); return $gen; }
This is fairly inefficient and is a hassle to do.
Efficiency may be ok, depends on the implementation. Alternative to
this FP style is an OOP approach that makes |this| for the generator
be an instance with a var referencing the generator iterator for that
instance.
Other methods rely on some sort of generator manager (shift the responsibility to the code handling the generators rather than within them) or using global generator vars (which would force the generators to be specific to the script containing those vars), both of which aren't ideal in the above use case.
I'm unaware of a Python solution to this issue.
Hard to standardize an argument; don't want another keyword.
Suggestions?
Brendan Eich wrote:
On Apr 22, 2007, at 1:12 AM, Yuh-Ruey Chen wrote:
There are a couple of issues I see with the generator proposal.
- What happens when send(x) is called on a newly created
generator? In Python, this results in an exception unless x is None.See developer.mozilla.org/es4/proposals iterators_and_generators.html#generators which contains this sentence:
"A generator must be started by a method call to next() or send (undefined). It is a TypeError to send a value other than undefined
to a newborn generator."
Ack, I didn't see that - I was only looking at the bulleted points above that statement.
- Is there any way to get a generator from within a generator
function? One use case for this would be asynchronous callbacks (can't
synchronous because of issue 2 noted above):// client calls this function somewhere function clientFoo() { let serverResponse = yield (server.asyncProcedure (clientFooGenerator)); print(serverResponse); }
// server.asyncProcedure calls this once it's finished, passing clientFooGenerator for gen function serverCallback(gen, retvals) { gen.send(retvals); }
The only way to do this right now is to wrap the generator function within a function that introduces the iterator to the generator function's scope:
function foo() { var $gen = function() { // generator code that can use $gen }(); return $gen; }
This is fairly inefficient and is a hassle to do.
Efficiency may be ok, depends on the implementation. Alternative to
this FP style is an OOP approach that makes |this| for the generator
be an instance with a var referencing the generator iterator for that
instance.
I thought of that, but if foo were a method, |this| would already be taken.
Other methods rely on some sort of generator manager (shift the responsibility to the code handling the generators rather than within them) or using global generator vars (which would force the generators to be specific to the script containing those vars), both of which aren't ideal in the above use case.
I'm unaware of a Python solution to this issue.
Hard to standardize an argument; don't want another keyword.
Suggestions?/be
I was thinking of adding another property to the arguments object. Perhaps, arguments.generator. The value for this property would be null (or undefined) if the function isn't a generator function.
- Yuh-Ruey Chen
Other methods rely on some sort of generator manager (shift the responsibility to the code handling the generators rather than within them) or using global generator vars (which would force the generators to be specific
to the script containing those vars), both of which aren't ideal in the
above use case.I'm unaware of a Python solution to this issue.
Hard to standardize an argument; don't want another keyword. Suggestions?
/be
I was thinking of adding another property to the arguments object. Perhaps, arguments.generator. The value for this property would be
null (or undefined) if the function isn't a generator function.
Something like this would be great. In practice, this particular
suggestion might be a tad unwieldy:
function foo() { var gen = arguments.generator; var command = new AsynchCommand(); command.onResult = function(result) { gen.send(result); } command.execute(); var result = yield; // do something with the result }
The unwieldy part being that you're required to set
arguments.generator to a local var because otherwise the arguments
object gets masked. But this could be simplified a little if you
could obtain a send() delegate directly from the generator object:
function foo() { var command = new AsynchCommand(); command.onResult = arguments.generator.delegate(); command.start(); var result = yield; // do something with the result }
Neil Mix wrote:
Hard to standardize an argument; don't want another keyword. Suggestions?
/be
I was thinking of adding another property to the arguments object. Perhaps, arguments.generator. The value for this property would be
null (or undefined) if the function isn't a generator function.Something like this would be great. In practice, this particular
suggestion might be a tad unwieldy:function foo() { var gen = arguments.generator; var command = new AsynchCommand(); command.onResult = function(result) { gen.send(result); } command.execute(); var result = yield; // do something with the result }
The unwieldy part being that you're required to set
arguments.generator to a local var because otherwise the arguments
object gets masked. But this could be simplified a little if you
could obtain a send() delegate directly from the generator object:function foo() { var command = new AsynchCommand(); command.onResult = arguments.generator.delegate(); command.start(); var result = yield; // do something with the result }
Wouldn't Function.prototype.bind (developer.mozilla.org/es4/proposals/static_generics.html) be sufficient for this?
function foo() { var command = new AsynchCommand(); command.onResult = arguments.generator.send.bind(arguments.generator); command.execute(); var result = yield; }
It's definitely wordier, but the user could also easily implement your delegate() method above by adding it to Function.prototype.
Alternatively, we could make gen.next() and gen.send() special in that they are already prebound to gen (e.g. gen.send.call(anotherGen) still results in gen.send()). Personally, I'd rather not do this - pretty much all builtin classes have unbound prototype methods rather than bound instance methods.
-Yuh-Ruey Chen
The unwieldy part being that you're required to set arguments.generator to a local var because otherwise the arguments object gets masked. But this could be simplified a little if you could obtain a send() delegate directly from the generator object:
Wouldn't Function.prototype.bind (developer.mozilla.org/es4/proposals/static_generics.html) be sufficient for this?
Yes, although a bit wordy as you point out.
It's definitely wordier, but the user could also easily implement your delegate() method above by adding it to Function.prototype.
I don't follow -- did you mean Generator.prototype?
Alternatively, we could make gen.next() and gen.send() special in that they are already prebound to gen (e.g. gen.send.call(anotherGen) still results in gen.send()). Personally, I'd rather not do this - pretty
much all builtin classes have unbound prototype methods rather than bound instance methods.
Agreed.
On Apr 22, 2007, at 9:04 PM, Yuh-Ruey Chen wrote:
Alternatively, we could make gen.next() and gen.send() special in that they are already prebound to gen (e.g. gen.send.call(anotherGen) still results in gen.send()).
This is a possibility, but it would change the |this| binding inside
the generator function from its initial value, captured when the
generator function was called to create a geneator-iterator.
The JS1.7 native methods for generators (next, send, etc.) throw if
called on the wrong type of |this|, so it's conceivable that ES4
could bind these methods to their generator-iterator and avoid such
type errors. But what you propose above requires re-binding |this| in
the saved execution context for the generator function, which is kept
inside the generator-iterator.
Personally, I'd rather not do this - pretty much all builtin classes have unbound prototype methods rather than bound instance methods.
Not all built-in classes have unbound prototype methods, and for good
reason in certain cases. String and Array methods are intentionally
generic, apart from to*String/valueOf -- those throw type errors if
misappropriated.
Class methods in ES4 are bound, so extracting a reference to one
forms a binding with the object from which the method ref was
extracted, supplying that object as |this| in subsequent calls via
the ref.
But the idea for generator next and send that the saved |this|, from
the original activation of the generator function, would be replaced
by the generator-iterator, is interesting. It provides the
convenience Yuh-Ruey is seeking. On the other hand, it overwrites the
|this| captured in the generator call. I think that's a fatal
(barely) flaw.
So still looking for a slick way to propagate the generator-iterator...
Neil Mix wrote:
It's definitely wordier, but the user could also easily implement your delegate() method above by adding it to Function.prototype.
I don't follow -- did you mean Generator.prototype
Yeah, that's what I meant...
Brendan Eich wrote:
On Apr 22, 2007, at 9:04 PM, Yuh-Ruey Chen wrote:
Alternatively, we could make gen.next() and gen.send() special in that they are already prebound to gen (e.g. gen.send.call(anotherGen) still results in gen.send()).
This is a possibility, but it would change the |this| binding inside
the generator function from its initial value, captured when the
generator function was called to create a geneator-iterator.The JS1.7 native methods for generators (next, send, etc.) throw if
called on the wrong type of |this|, so it's conceivable that ES4
could bind these methods to their generator-iterator and avoid such
type errors. But what you propose above requires re-binding |this| in
the saved execution context for the generator function, which is kept
inside the generator-iterator.Personally, I'd rather not do this - pretty much all builtin classes have unbound prototype methods rather than bound instance methods.
Not all built-in classes have unbound prototype methods, and for good
reason in certain cases. String and Array methods are intentionally
generic, apart from to*String/valueOf -- those throw type errors if
misappropriated.Class methods in ES4 are bound, so extracting a reference to one
forms a binding with the object from which the method ref was
extracted, supplying that object as |this| in subsequent calls via
the ref.But the idea for generator next and send that the saved |this|, from
the original activation of the generator function, would be replaced
by the generator-iterator, is interesting. It provides the
convenience Yuh-Ruey is seeking. On the other hand, it overwrites the
|this| captured in the generator call. I think that's a fatal
(barely) flaw.So still looking for a slick way to propagate the generator-iterator...
/be
Replacing |this| is not what I'm suggesting - in fact, I rejected it for the reason you give. |this| within the generator function is left untouched - it's not replaced with anything. The generator-iterator is accessed within the generator function via arguments.generator. Then I proposed that next() and send() are bound class methods rather than unbound prototype methods. I'm not exactly sure how class methods are implemented, but in ES3, this is practically equivalent to:
// constructor for generator-iterators function Generator(...) { ... var self = this; this.next = function() { return Generator.prototype.next.call(self); }; this.send = function(x) { return Generator.prototype.send.call(self, x); }; ... }
...with the added benefit that Generator.prototype.send/next can still be overwritten meaningfully. But as I said before, it seems odd that only next() and send() are special, or that Generator (and possibly its counterpart, Iterator) is special with respect to the builtin constructors already in ES3.
In any case, I've been experimenting with Python lately to see if there's some convenient solution for the intended use case (async calls and returns), since Python's generator functions also lack a way to access their "parent" generator-iterators. One library I've been looking at is the Twisted framework, or rather it's Deferred component. It's solution to this issue is an inlineCallbacks function intended to wrap a generator function within some sort of generator manager: twistedmatrix.com/documents/current/api/twisted.internet.defer.html#inlineCallbacks.
-Yuh-Ruey Chen
On Apr 23, 2007, at 9:39 AM, Yuh-Ruey Chen wrote:
Replacing |this| is not what I'm suggesting - in fact, I rejected
it for the reason you give. |this| within the generator function is left untouched - it's not replaced with anything.
Ok, good to agree.
The generator-iterator is accessed within the generator function via arguments.generator.
This is still a bit awkward for the reason given by Neil, and with
rest parameters, we are trying to avoid arguments being a commonly
used object (so that some day it might become deprecate-able -- it's
far from that now).
Then I proposed that next() and send() are bound class methods rather than unbound prototype methods.
Right, and I pointed out how they're uselessly re-bindable (to any
object), but that if re-bound (I mean |this| binding here) to a non-
generator-iterator object, you'll get a type error trying to call them.
I'm not exactly sure how class methods are implemented, but in ES3, this is practically equivalent to:
// constructor for generator-iterators function Generator(...) { ... var self = this; this.next = function() { return Generator.prototype.next.call (self); }; this.send = function(x) { return Generator.prototype.send.call (self, x); }; ... }
...with the added benefit that Generator.prototype.send/next can still be overwritten meaningfully.
That's not always a benefit. See bugzilla.mozilla.org show_bug.cgi?id=354750.
But as I said before, it seems odd that only next() and send() are special, or that Generator (and possibly
its counterpart, Iterator) is special with respect to the builtin constructors already in ES3.
Sorry, not sure what you mean here. There is no Iterator nominal type
proposed for ES4. There's no Generator or Iterator in ES3.
Locking up methods against AOP-style overriding via mutable prototype
bindings is sometimes the right thing for integrity, which is a
security property.
In any case, I've been experimenting with Python lately to see if there's some convenient solution for the intended use case (async
calls and returns), since Python's generator functions also lack a way to access their "parent" generator-iterators. One library I've been
looking at is the Twisted framework, or rather it's Deferred component. It's solution to this issue is an inlineCallbacks function intended to
wrap a generator function within some sort of generator manager: twistedmatrix.com/documents/current/api twisted.internet.defer.html#inlineCallbacks.
Yes, this is familiar too from MochiKit, which was inspired by
Twisted. But the question you raise remains: should there be a more
convenient, primitive way to denote the current generator-iterator
from within its generator function?
Brendan Eich wrote:
On Apr 23, 2007, at 9:39 AM, Yuh-Ruey Chen wrote:
The generator-iterator is accessed within the generator function via arguments.generator.
This is still a bit awkward for the reason given by Neil, and with
rest parameters, we are trying to avoid arguments being a commonly
used object (so that some day it might become deprecate-able -- it's
far from that now).
Well then I'm stumped. We can't reuse |this|, shouldn't introduce a new keyword, and are trying to disuage usage |arguments|. Using the generator function itself (like how recursive functions reference themselves) is a non-starter too.
A comment on |arguments| though: Although I think it is more elegant to use the new rest parameters than the arguments object, the arguments object does have a very useful property that I use all the time: arguments.callee. I often use this within recursive functions, since if I ever rename my function (which is common with helper functions), I don't need to mess with the function body at all. So even if arguments is deprecated, I would still want some |arguments.callee| equivalent.
Then I proposed that next() and send() are bound class methods rather than unbound prototype methods.
Right, and I pointed out how they're uselessly re-bindable (to any
object), but that if re-bound (I mean |this| binding here) to a non- generator-iterator object, you'll get a type error trying to call them.I'm not exactly sure how class methods are implemented, but in ES3, this is practically equivalent to:
// constructor for generator-iterators function Generator(...) { ... var self = this; this.next = function() { return Generator.prototype.next.call (self); }; this.send = function(x) { return Generator.prototype.send.call (self, x); }; ... }
...with the added benefit that Generator.prototype.send/next can still be overwritten meaningfully.
That's not always a benefit. See bugzilla.mozilla.org show_bug.cgi?id=354750.
But as I said before, it seems odd that only next() and send() are special, or that Generator (and possibly
its counterpart, Iterator) is special with respect to the builtin constructors already in ES3.Sorry, not sure what you mean here. There is no Iterator nominal type
proposed for ES4. There's no Generator or Iterator in ES3.Locking up methods against AOP-style overriding via mutable prototype
bindings is sometimes the right thing for integrity, which is a
security property.
I didn't mean that Generator or Iterator were already in ES3 - I was just saying that existing ES3 builtin constructors already use prototype methods, so it would make sense for Generator and Iterator to follow suite. But in this case, the next() and send() being special may be warranted in light of the integrity concerns you raise.
In any case, I've been experimenting with Python lately to see if there's some convenient solution for the intended use case (async
calls and returns), since Python's generator functions also lack a way to access their "parent" generator-iterators. One library I've been
looking at is the Twisted framework, or rather it's Deferred component. It's solution to this issue is an inlineCallbacks function intended to
wrap a generator function within some sort of generator manager: twistedmatrix.com/documents/current/api twisted.internet.defer.html#inlineCallbacks.Yes, this is familiar too from MochiKit, which was inspired by
Twisted. But the question you raise remains: should there be a more
convenient, primitive way to denote the current generator-iterator
from within its generator function?/be
That was what I was trying to answer. Playing devil's advocate here. Are there any other use cases where accessing the current generator from within a generator function would be useful (and can't otherwise be "easily" simulated)? If the only use case I was thinking of it for can already be handled well enough (albeit somewhat awkwardly), that functionality may not be needed. Furthermore, if we really want to support async programming seamlessly in the language, I think it would take more than just arguments.generator (or the equivalent).
-Yuh-Ruey Chen
One library I've been looking at is the Twisted framework, or rather it's Deferred component. It's solution to this issue is an inlineCallbacks function intended to
wrap a generator function within some sort of generator manager: twistedmatrix.com/documents/current/api twisted.internet.defer.html#inlineCallbacks.Yes, this is familiar too from MochiKit, which was inspired by
Twisted. But the question you raise remains: should there be a more
convenient, primitive way to denote the current generator-iterator
from within its generator function?
I think yes. Perhaps I'm over-generalizing, but it seems to me that
because python has blocking I/O, inlineCallbacks is the outlier use-
case. I don't think the same can be said of JavaScript since it
typically has no blocking I/O.
So if new keywords are verboten, and arguments.generator is verboten
(which would be better than nothing, I say), then what's left? Is a
static method on the Generator class a reasonable option?
On Apr 23, 2007, at 10:55 AM, Neil Mix wrote:
So if new keywords are verboten, and arguments.generator is verboten (which would be better than nothing, I say), then what's left? Is a static method on the Generator class a reasonable option?
I wouldn't say that arguments.generator is verboten -- I was just
arguing that arguments might not be the best place to stash this
generator property. Your point about having to capture it also
motivates the search for another way.
A static method would avoid the shadowing problem you raised. It
would reflect on the current call stack, presumably finding the top-
most generator activation, and extracting the generator-iterator
reference (which generally must be kept somewhere reachable from that
activation, to keep the generator-iterator alive). It seems slightly
more winning to me, but still leaves an odd taste. Thoughts?
On Apr 23, 2007, at 1:18 PM, Brendan Eich wrote:
A static method would avoid the shadowing problem you raised. It
would reflect on the current call stack, presumably finding the top- most generator activation, and extracting the generator-iterator
reference (which generally must be kept somewhere reachable from
that activation, to keep the generator-iterator alive). It seems
slightly more winning to me, but still leaves an odd taste. Thoughts?
It seems inconsistent that the current generator would be fetched via
a static method on the Generator class, while the current function
would be fetched via arguments.callee. Perhaps that's an argument
for deprecating arguments.callee in favor of a static method on the
Function class? So we could have a consistently odd taste.
Neil Mix wrote:
On Apr 23, 2007, at 1:18 PM, Brendan Eich wrote:
A static method would avoid the shadowing problem you raised. It
would reflect on the current call stack, presumably finding the top- most generator activation, and extracting the generator-iterator
reference (which generally must be kept somewhere reachable from
that activation, to keep the generator-iterator alive). It seems
slightly more winning to me, but still leaves an odd taste. Thoughts?It seems inconsistent that the current generator would be fetched via
a static method on the Generator class, while the current function
would be fetched via arguments.callee. Perhaps that's an argument
for deprecating arguments.callee in favor of a static method on the
Function class? So we could have a consistently odd taste.
I agree. If the current generator is accessed via a static method, then the same should apply to the current function accessor, especially if you intend to deprecate |arguments| in the future. The syntax is rather un-ES like, but if |arguments| is out of the picture, then it's better than introducing a new keyword.
On Apr 24, 2007, at 11:48 AM, Yuh-Ruey Chen wrote:
I agree. If the current generator is accessed via a static method,
then the same should apply to the current function accessor, especially if you intend to deprecate |arguments| in the future. The syntax is
rather un-ES like, but if |arguments| is out of the picture, then it's better than introducing a new keyword.
Of course arguments is nowhere near out of the picture, and won't be
until rest params take over and old code is rewritten or retired --
which may not happen by any particular date. So I withdraw my
objection to arguments.generator on account of arguments being
frowned upon. What's the point of frowning at a brick wall?
The other reason (shadowing of arguments reducing convenience)
stands, but Generator.current is un-ES-like, I agree -- not in its
context-free syntax as in its meaning. Function.current instead of
arguments.callee would be the parallel construction. So unless
something better pops up, arguments.generator is still winning in my
view.
On 4/22/07, Neil Mix <nmix at pandora.com> wrote:
Other methods rely on some sort of generator manager (shift the responsibility to the code handling the generators rather than within them) or using global generator vars (which would force the generators to be specific to the script containing those vars), both of which aren't ideal in the above use case.
I'm unaware of a Python solution to this issue.
Hard to standardize an argument; don't want another keyword. Suggestions?
/be
I was thinking of adding another property to the arguments object. Perhaps, arguments.generator. The value for this property would be null (or undefined) if the function isn't a generator function.
Something like this would be great. In practice, this particular suggestion might be a tad unwieldy:
function foo() { var gen = arguments.generator; var command = new AsynchCommand(); command.onResult = function(result) { gen.send(result); } command.execute(); var result = yield; // do something with the result }
The unwieldy part being that you're required to set arguments.generator to a local var because otherwise the arguments object gets masked.
Using some ES4 facilities for structuring and desugaring it's not quite as awful as it may look at first blush:
function foo() { var command = new AsynchCommand(); command.onResult = let (gen = arguments.generator) function(result) gen.send(result); command.execute(); var result = yield; // do something with the result }
On 4/25/07, Brendan Eich <brendan at mozilla.org> wrote:
On Apr 24, 2007, at 11:48 AM, Yuh-Ruey Chen wrote:
I agree. If the current generator is accessed via a static method, then the same should apply to the current function accessor, especially if you intend to deprecate |arguments| in the future. The syntax is rather un-ES like, but if |arguments| is out of the picture, then it's better than introducing a new keyword.
Of course arguments is nowhere near out of the picture, and won't be until rest params take over and old code is rewritten or retired -- which may not happen by any particular date. So I withdraw my objection to arguments.generator on account of arguments being frowned upon. What's the point of frowning at a brick wall?
The other reason (shadowing of arguments reducing convenience) stands, but Generator.current is un-ES-like, I agree -- not in its context-free syntax as in its meaning. Function.current instead of arguments.callee would be the parallel construction. So unless something better pops up, arguments.generator is still winning in my view.
In a sense, 'this' is really a keyword shorthand for something we can think of as 'arguments.receiver'.
I would dearly like to see the arguments object fall into disuse. Yuh-Ruey writes he likes to use 'arguments.callee' to reference the current function regardless of its name; now we have use cases for 'arguments.generator'. IMO this is not moving in the right direction :-)
Perhaps what we need is a little more syntax without more keywords. Let 'function' and 'generator' be contextual keywords that can appear following 'this' without an intervening newline (obviously "function" is a keyword already). Then
this function
names the current function without having to worry about the name of the function or about looking it up in the nesting scope (ie, it's like arguments.callee), and
this generator
names the generator object as the proposed arguments.generator would.
On May 7, 2007, at 3:45 AM, Lars T Hansen wrote:
this function this generator
Sweet!
Lars T Hansen wrote:
In a sense, 'this' is really a keyword shorthand for something we can think of as 'arguments.receiver'.
I would dearly like to see the arguments object fall into disuse. Yuh-Ruey writes he likes to use 'arguments.callee' to reference the current function regardless of its name; now we have use cases for 'arguments.generator'. IMO this is not moving in the right direction :-)
Perhaps what we need is a little more syntax without more keywords. Let 'function' and 'generator' be contextual keywords that can appear following 'this' without an intervening newline (obviously "function" is a keyword already). Then
this function
names the current function without having to worry about the name of the function or about looking it up in the nesting scope (ie, it's like arguments.callee), and
this generator
names the generator object as the proposed arguments.generator would.
--lars
I like it. What's the operator precedence?
I wonder if this can be extended to iterators in general. Since generator functions are essentially sugar (albeit really awesome sugar + send() + close()) for iterators, |this generator| could be generalized to |this iterator|. Although generator-iterators are different from normal iterators, they are iterators nonetheless. I can't think of any use cases of the top of my head, and I'm not sure how it would be done, considering that iterators are a protocol, not some class (i.e. I'm not sure how ES can determine |this iterator| when the user calls iter.next() for a user-defined iterator).
On May 8, 2007, at 6:09 PM, Yuh-Ruey Chen wrote:
Lars T Hansen wrote:
this generator
names the generator object as the proposed arguments.generator would.
--lars
I like it. What's the operator precedence?
I suggested primary, so one can write |this function.length| instead
of having to parenthesize, or unary, by analogy to delete, void, etc.
(so parentheses are required for a . or similar member operator).
I wonder if this can be extended to iterators in general. Since generator functions are essentially sugar (albeit really awesome
sugar + send() + close()) for iterators, |this generator| could be generalized to |this iterator|. Although generator-iterators are different from normal iterators, they are iterators nonetheless. I can't think of any use cases of the top of my head, and I'm not sure how it would be
done, considering that iterators are a protocol, not some class (i.e. I'm
not sure how ES can determine |this iterator| when the user calls iter.next() for a user-defined iterator).
You must mean in a function implementing the next property of
IteratorType, but then it's easy to find the iterator via |this|. On
the other hand, the generator's |this| may be bound arbitrarily at
the time the generator function is called (e.g. to the global object,
or to some class instance if the generator function is a method). So
we're really talking about a case that's special to generators here.
Lars T Hansen wrote:
this generator
names the generator object as the proposed arguments.generator would.
On May 8, 2007, at 6:09 PM, Yuh-Ruey Chen wrote:
I like it. What's the operator precedence?
On 09/05/07, Brendan Eich <brendan at mozilla.org> wrote:
I suggested primary, so one can write |this function.length| instead of having to parenthesize, or unary, by analogy to delete, void, etc. (so parentheses are required for a . or similar member operator).
Hmm. If unary operator, what does this do?
this function(...){...}(...)
Is that equivalent to the following:
this (function(...){...}(...))
or - in statement context - to this:
(this function)(...);
{...}
(...);
or - in expression context - syntax error, because:
(this function)(...)
directly followed by an object literal doesn't make sense?
Personally I would prefer it to be a primary expression.
There are a couple of issues I see with the generator proposal.
What happens when send(x) is called on a newly created generator? In Python, this results in an exception unless x is None.
What happens when a generator calls next/send on itself e.g.
var $gen; function foo() { $gen.next(); // what should this do? yield; } $gen = foo(); $gen.next();
In Python, this results in an exception.
// client calls this function somewhere function clientFoo() { let serverResponse = yield (server.asyncProcedure(clientFooGenerator)); print(serverResponse); }
// server.asyncProcedure calls this once it's finished, passing clientFooGenerator for gen function serverCallback(gen, retvals) { gen.send(retvals); }
The only way to do this right now is to wrap the generator function within a function that introduces the iterator to the generator function's scope:
function foo() { var $gen = function() { // generator code that can use $gen }(); return $gen; }
This is fairly inefficient and is a hassle to do. Other methods rely on some sort of generator manager (shift the responsibility to the code handling the generators rather than within them) or using global generator vars (which would force the generators to be specific to the script containing those vars), both of which aren't ideal in the above use case.
I'm unaware of a Python solution to this issue.