I'd like to contribute what I think are three not-yet-voiced arguments to
this discussion:
1) Iterators != Collections
First, let me briefly restate what I preceive to be the main point of
contention: if we overload for-in, then client code that previously executed
"for (x in obj)" with obj being a regular object, and now finds itself in
Harmony-land where obj _is changed to_ an iterator, then "for (x in obj)"
may confuse the client, if the iterator produces non-string values.
Coming back to my point that regular objects don't just become iterator
objects overnight: it was not yet emphasized that in Dave's iterators API,
iterators are essentially considered as proxies for collections, usually
even constructed for one-off iteration use only. That is: the original
collection object itself is _not_ iterable, and that means that clients that
hold on to just the collection object can never become "for-in"-confused.
I feel that this tremendously reduces the risk of confusion, even though it
is still possible, and I have no empirical evidence to back this up.
However, reasoning by analogy: in pre 1.5 Java, one also asked a Collection
for an Iterator, then iterated using the Iterator, not the original
Collection. Of course, Java's static type system virtually rules out all
confusion since Iterator is not a subtype of Collection, but I would argue
that in this case, it was the _idiom_ more than the type system that avoided
confusion. Java programmers didn't get confused because it was idiomatic to
write: "for (Iterator it = coll.iterator(); it.hasNext(); ) { ... it.next()
... }"
In Harmony, as Dave has shown, it would be idiomatic to use iterators as
follows: "for (var x in coll.iterator()) { ... }"
Note: if a harmony programmer would, in a stroke of forgetfulness, forget
the ".iterator()" part, he'll get _the usual_ for-in enumeration behavior
for 'coll', since 'coll' is a normal collection object, not an iterator. Of
course, if the programmer meant to do iteration, this is a bug. But, more
importantly, if the programmer wasn't forgetful and he meant what he wrote
(i.e. enumerate the keys of the collection object), it'll do the right
thing.
If this is the idiom around which Harmony iterators are based, I would find
it unlikely (drawing from my experience with Java Iterators) that Harmony
programmers will start passing out references to coll.iterator() to clients
where they meant to pass 'coll'. I would only expect to see .iterator() in
or close to a for-in loop that immediately 'uses up' the iterator.
2) Avoid Iterators that pretend to be Collections
Now, in light of the above, one reservation I have about Iterators is the
API method called Iterator.for(coll), which creates and returns an iterator
that is _also_ a forwarding proxy to coll. This method gives an incentive to
users to pass this proxy where they could have also passed 'coll' to a
client. I think this is a mistake. Iterators should be distinct from
collections, as in Java. If iterators are indeed substitutable for
collection objects, then the chance of confusing clients becomes much
greater than it should be, effectively undermining argument 1)
3) Who is to blame for client confusion
Andreas, in the thread on guard syntax, observed that there is a very thin
line between a proxy enumerating duplicate/nonsensical keys (which is
possible using just the enumerate() trap, and perhaps even necessary to
emulate quirky host objects) and producing arbitrary values. Clients may get
confused either way.
It seems to me that we attribute different "weights" to these confusions
based on the perceived use of proxies. Why is this? Here's my take on it:
- proxies that use enumerate() to generate faux and/or duplicate keys are
considered by us to somehow "abuse" the trap. We cannot prevent anyone from
writing bad proxies, but we also don't expect code that does this to be
idiomatic, or part of a popular library. Hence, even though these proxies
may _deliberately_ confuse client code, we expect the consequences to be
"contained". It's obvious who to blame for the confusion in this case.
- proxies that use iterate() to generate streams of arbitrary values are
considered a legitimate use case. Under the hypothesis that
strawman:iterators would become part of the language, it is our expectation
that such proxies _will_ become widespread. When they do, client code that
previously executed "for (x in obj)" might now find obj bound to an iterator
and get confused. Hence, even though it wasn't the intention of the iterator
proxy to confuse the client, the confusion happened anyway. This is
considered more problematic, because it's hard to figure out who to blame.
Both client and proxy meant to cooperate, but failed.
In short:
- Idiomatic use of iterators should prevent client code confusion in the
common case.
- Objects don't become iterators overnight. When porting ES5 code to
Harmony, I would expect ES5 objects used as collections to now get an
"iterator()" method that returns a distinct iterator object. It would be
weird for the collection object _itself_ to become an iterator. The
iterators API, without Iterator.for, makes the first option the path of
least resistance, by far.
Cheers,
Tom
2010/11/23 Waldemar Horwat <[email protected]>
> On 11/23/10 13:05, David Herman wrote:
>
>> How would a new object abstraction T customize them just for instances of
>>>> T?
>>>>
>>>
>>> By writing its own custom iteration protocol via proxies with the
>>> iterate() trap implemented appropriately. E.g.:
>>>
>>> function MyCollection() { }
>>> MyCollection.prototype = {
>>> iterator: function() {
>>> var self = this;
>>> return Proxy.create({
>>> iterate: function() { ... self ... },
>>> ...
>>> });
>>> }
>>> }
>>>
>>
>> I left out the last step: clients would then use this via:
>>
>> var coll = new MyCollection();
>> ...
>> for (var x in coll.iterator()) {
>> ...
>> }
>>
>> Dave
>>
>
> That misses the point of my question. Sure you can define the meaning of
> for (k in MyNewFunction(o))
> or
> for (k in o.MyMethod())
>
> However, I asked how a new object abstraction T would customize "keys",
> "values", and "properties" just for instances of T so that instances of T
> could be usable under the standard pattern:
>
>
> for (k in keys(o)) ...
> for (v in values(o)) ...
> for ([k, v] in properties(o)) ...
>
> Waldemar
>
> _______________________________________________
> es-discuss mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss