On Tue, Mar 23, 2010 at 2:45 PM, Brendan Eich <brendan at mozilla.org> wrote:
On Mar 23, 2010, at 2:17 PM, Kris Kowal wrote:
Aside: I hope we can resolve MarkM's suspicions of composability
problems with generators; they are one of my very favorite features of
Python: like UNIX pipes, composability is their middle-name. The PEP
he referenced some time ago just provides more terse syntax for
chained generators; I find that this is a nicety but not a
demonstration that the simple "yield" is flawed.
Yes, just because something doesn't sing and dance doesn't mean it is not a
good actor ;-).
I also hope that
ECMAScript opts to follow the Python iterator protocol more closely
than JavaScript 1.6—for chained lazy iterations, "next" needs to be
the most discrete layer of the protocol, with "forEach" implemented in
terms thereof.
JS1.6 was forEach and other Array extras.
JS1.7 introduced generators and iterators, and yes, we didn't incompatibly
change the Array extras to treat iterators as arrays. We also didn't
introduce new libraries, e.g. Python itertools workalikes. It's easy enough
to do this in user-land, and hard for the TC39 committee to get library
design right.
Thanks for the clarification.
At this point how would you make forEach and other Array extras work on
iterators?
I'm getting all sorts of alarms going off in my head to the tune of
"this is all obvious; I probably missed something important. Like,
this is how it works already." I've heard complaints about for each
loops stalling to collect though, so here are my thoughts about
iterators anyway:
I was just looking at some transcoding stream stuff, which follows the
general form of Python's iterator protocol. The notion is that "next"
is the most atomic unit of the iterator protocol; if you have an
object that implements "iterator", that returns an object that
implements "next", you get "forEach" and friends for free. This is
important because "next" is "pausable" (or progress on explicit
request, really), and "forEach" is not. This means that you can
create incrementally iterable generics like "mapIterator" and thereby
create chains of decorated iterators that can operate on iterations of
indefinite length, and "forEach" doesn't have to collect the values of
the iterable before entering its loop.
@this {{iterator}} any iterable object. iterator must
return an object with a next method that returns the
next element of the iteration or throws StopIteration.
*/
SomethingAutoIterable.prototype.forEach = function (block, that) {
var line;
var iterator = this.iterator();
while (true) {
try {
line = iterator.next();
} catch (exception) {
if (exception === StopIteration)
break;
throw exception;
}
block.call(that, line);
}
};
If you're crazy, can optimize the crap out of the try/catches
(presumably by removing them for a small set of common types), and
want to take things one step further than Python, you can implement
both "continue" and "break" semantics by adding SkipIteration.
…forEach = function (relation, that) {
var iterator = this.iterator();
try {
while (true) {
try {
relation.call(that, iterator.next());
} catch (exception) {
if (exception !== SkipIteration) {
throw exception;
}
}
}
} catch (exception) {
if (exception !== StopIteration) {
throw exception;
}
}
};
And, if you're really crazy, you can implement breaking and
continuing outer loops by associating individual StopIteration and
SkipIteration instances with each iterator. By really crazy, I mean
that my PEP got shot down, so I wouldn't be surprised by a repeat
success.
Kris Kowal
[moving this thread onto es-discuss]
On Tue, Mar 23, 2010 at 2:45 PM, Brendan Eich <brendan at mozilla.org> wrote:
> On Mar 23, 2010, at 2:17 PM, Kris Kowal wrote:
>
>> Aside: I hope we can resolve MarkM's suspicions of composability
>> problems with generators; they are one of my very favorite features of
>> Python: like UNIX pipes, composability is their middle-name. The PEP
>> he referenced some time ago just provides more terse syntax for
>> chained generators; I find that this is a nicety but not a
>> demonstration that the simple "yield" is flawed.
>
> Yes, just because something doesn't sing and dance doesn't mean it is not a
> good actor ;-).
>
>> I also hope that
>> ECMAScript opts to follow the Python iterator protocol more closely
>> than JavaScript 1.6—for chained lazy iterations, "next" needs to be
>> the most discrete layer of the protocol, with "forEach" implemented in
>> terms thereof.
>
> JS1.6 was forEach and other Array extras.
>
> JS1.7 introduced generators and iterators, and yes, we didn't incompatibly
> change the Array extras to treat iterators as arrays. We also didn't
> introduce new libraries, e.g. Python itertools workalikes. It's easy enough
> to do this in user-land, and hard for the TC39 committee to get library
> design right.
Thanks for the clarification.
> At this point how would you make forEach and other Array extras work on
> iterators?
I'm getting all sorts of alarms going off in my head to the tune of
"this is all obvious; I probably missed something important. Like,
this is how it works already." I've heard complaints about for each
loops stalling to collect though, so here are my thoughts about
iterators anyway:
I was just looking at some transcoding stream stuff, which follows the
general form of Python's iterator protocol. The notion is that "next"
is the most atomic unit of the iterator protocol; if you have an
object that implements "iterator", that returns an object that
implements "next", you get "forEach" and friends for free. This is
important because "next" is "pausable" (or progress on explicit
request, really), and "forEach" is not. This means that you can
create incrementally iterable generics like "mapIterator" and thereby
create chains of decorated iterators that can operate on iterations of
indefinite length, and "forEach" doesn't have to collect the values of
the iterable before entering its loop.
/***
* @this {Object}
*/
SomethingAutoIterable.prototype.iterator = function () {
return this;
};
/***
* @this {{iterator}} any iterable object. `iterator` must
* return an object with a `next` method that returns the
* next element of the iteration or throws `StopIteration`.
*/
SomethingAutoIterable.prototype.forEach = function (block, that) {
var line;
var iterator = this.iterator();
while (true) {
try {
line = iterator.next();
} catch (exception) {
if (exception === StopIteration)
break;
throw exception;
}
block.call(that, line);
}
};
If you're crazy, can optimize the crap out of the try/catches
(presumably by removing them for a small set of common types), and
want to take things one step further than Python, you can implement
both "continue" and "break" semantics by adding SkipIteration.
…forEach = function (relation, that) {
var iterator = this.iterator();
try {
while (true) {
try {
relation.call(that, iterator.next());
} catch (exception) {
if (exception !== SkipIteration) {
throw exception;
}
}
}
} catch (exception) {
if (exception !== StopIteration) {
throw exception;
}
}
};
And, if you're *really* crazy, you can implement breaking and
continuing outer loops by associating individual StopIteration and
SkipIteration instances with each iterator. By really crazy, I mean
that my PEP got shot down, so I wouldn't be surprised by a repeat
success.
Kris Kowal
[moving this thread onto es-discuss]
On Tue, Mar 23, 2010 at 2:45 PM, Brendan Eich <brendan at mozilla.org> wrote:
Thanks for the clarification.
I'm getting all sorts of alarms going off in my head to the tune of "this is all obvious; I probably missed something important. Like, this is how it works already." I've heard complaints about for each loops stalling to collect though, so here are my thoughts about iterators anyway:
I was just looking at some transcoding stream stuff, which follows the general form of Python's iterator protocol. The notion is that "next" is the most atomic unit of the iterator protocol; if you have an object that implements "iterator", that returns an object that implements "next", you get "forEach" and friends for free. This is important because "next" is "pausable" (or progress on explicit request, really), and "forEach" is not. This means that you can create incrementally iterable generics like "mapIterator" and thereby create chains of decorated iterators that can operate on iterations of indefinite length, and "forEach" doesn't have to collect the values of the iterable before entering its loop.
/***
/***
iterator
mustnext
method that returns theStopIteration
. */ SomethingAutoIterable.prototype.forEach = function (block, that) { var line; var iterator = this.iterator(); while (true) { try { line = iterator.next(); } catch (exception) { if (exception === StopIteration) break; throw exception; } block.call(that, line); } };If you're crazy, can optimize the crap out of the try/catches (presumably by removing them for a small set of common types), and want to take things one step further than Python, you can implement both "continue" and "break" semantics by adding SkipIteration.
…forEach = function (relation, that) { var iterator = this.iterator(); try { while (true) { try { relation.call(that, iterator.next()); } catch (exception) { if (exception !== SkipIteration) { throw exception; } } } } catch (exception) { if (exception !== StopIteration) { throw exception; } } };
And, if you're really crazy, you can implement breaking and continuing outer loops by associating individual StopIteration and SkipIteration instances with each iterator. By really crazy, I mean that my PEP got shot down, so I wouldn't be surprised by a repeat success.
Kris Kowal