-
Notifications
You must be signed in to change notification settings - Fork 34
Could we make iterator helpers more ergonomic for working with iterables? #78
Comments
All iterators are iterable, by convention. |
In other words, if |
I'd be surprised if Number.range() didn't return an iterator (e.g. I'd expect |
The only justification I've understood for why I'm not sure if my initial comment was clear--I understand that the |
|
Let's keep this issue scoped to the topic of, how should iterator helpers work with iterables. Arrays should be a concrete enough example to work with, right? It's useful to deal with them in a lazy/streaming fashion sometimes, right? We can discuss |
@littledan the topic has been discussed ad nauseam in this repo, I'd encourage you to read over some of the closed issues on iterables (for example, #8, #68, #18) |
Thanks for those cross-references. I haven't yet found any discussion in the issues or in the committee about leaving this proposal to be primarily about iterators, but also to improve the conveniences for working with iterables, beyond To be clear, I'm definitely happy about the inclusion of Do you see downsides to adding static methods to |
I don't see any benefit to adding those functions. I think they would only confuse people about iterable and iterator reusability. |
I have the same feeling with @littledan , basically it seems all iterables should have |
@hax I don't see how first-class protocols are connected. What do you mean? |
@littledan I mean, it seems the helpers should be iterable helpers instead of iterator helpers. But how to make a concrete class (especially those already exist classes --- which hard to change the prototype chain) get these iterable helpers? Maybe mixin or first-class protocol proposals (personally i prefer first-class protocol than mixin, but first-class protocol itself do not have ergonomic syntax yet, so I'm still thinking...) |
Umm some iterators don't inherited from Iterator.prototype and Iterables have their own iterator in general (a class that iterable) Maybe Iterator.from is the only solution.. m |
I believe it is - “iterable” is an adjective, not a noun, by which i mean, it has a single interface that categorizes it as such - one that can not be enlarged. As such, iterables (or thenables, or toStringables or toJSONables etc) can’t be gifted extra methods, because they’re not an identity, they’re a shared trait. Iterators, however, while they could be described the same (they have a next method) by convention all inherit from Iterator.prototype, which Iterator.from helps wrap things with. |
What if we let iterator helpers accept iterable as it's "this" value. When "this" is not a iterator but iterable, it automatically call the Symbol.iterator. Example: class X extends Iterator {
// inherit all iterator helpers from the extends clause
[Symbol.iterator]: ....
}
new X().take()
// Implicitly call X.@@iterator |
This issue really seems to just be rehashing things from a year ago so I'm going to close it out. |
@devsnek I understand that this problem may have been discussed many times and you may already have a conclusion. But if a problem was raised again and again, it may be a signal that we may miss something, at least we need an open place to continue the discussion. |
@ljharb I don't know how adjective/noun have difference in this problem. For me, "iterable" are just objects implement Iterable interface, and "iterator" are objects implement Iterator interface.
I think this is the only difference, that builtin iterators have "IteratorPrototype" as a "shared trait" installed. The interesting part is, if we look at "IteratorPrototype", it only have If we have the convention that all iterators should inherit from IteratorPrototype (to be honest, I don't see any js articles/books stress this point before), then it means Iterator should be the subtype/subclass of Iterable. So I think the suggested solution from @Jack-Works just works. Essentially it just change IteratorPrototype from the concept of the shared trait (or abstract base class) of Iterator to the shared trait (or abstract base class) of Iterable. Because iterators are also iterable, they of coz could "inherit" all helpers from the shared trait of Iterable. The only left problem is can we change the name of "IteratorPrototype" to "IterablePrototype" or "prototypeForBothIterableAndIterator" to make it clear 😆 |
The difference is that it's a simple transformation to change an iterator to one that inherits from Iterator.prototype; "iterables" include strings, maps, sets, arrays, etc, so it makes no sense to try to construct an inheritance hierarchy for all iterables. |
@ljharb I don't think it makes no sense. I mean, "construct an inheritance hierarchy for all iterables" sounds like crazy, but it's just the limitation of the language which only have single-root inheritance (include current JS), that's why I mention first-class protocol or mixin proposal. The ideal solution would be having a IterableProtocol (or IterableMixin) and IteratorProtocol (or IteratorMixin). But I think @Jack-Works 's solution is a workable solution as a tradeoff before we figure out how JS can have protocol or mixin in langauge. |
Given that it's the limitation of the language, it wouldn't make sense to design it that way - especially not if it depends on stage 1 proposals. |
I'm confused, @Jack-Works 's solution does not depend on stage 1 protocol/mixin proposals... |
I'm still talking about the "Iterables" approach. @Jack-Works's solution is one I advocated for here, and it's what the spec already does in Since all the prototype methods throw when the receiver lacks the [[Iterated]] internal slot, it would be a feasible follow-on proposal to relax that when the receiver was an iterable. |
Hope someone could add some explanation about such important semantic in README! |
Another thought: Object.assign({}, obj);
( { ...obj } );
Array.from(iterable); // equal to
[...iterable];
Iterator.from(iterable); // equal to
// ??? The missing syntax Add a new syntax to convert an I don't know what the syntax should be like but this may be a solution to this problem. |
In the
Number.range
proposal, iterator helpers are cited as a big reason why that API would vend an iterator, rather than an iterable tc39/proposal-iterator.range#17 . This makes me feel a bit uneasy; I thought the general JS convention for these things would be iterables. I wonder if there's something we could do in iterator helpers to make it more ergonomic to use them than requiring all uses to start withIterator.from
(which may be considered not ergonomic enough).Here's an idea: For each iterator method, we make a static version for
Iterator
, e.g.,Iterator.take
would callSymbol.iterator
on its first argument, and then pass the rest of the arguments on to thetake
method called on the result. So, ifNumber.range
returns an iterable, then the examples could be written as follows (all other examples remain unchanged):The text was updated successfully, but these errors were encountered: