-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Why an iterator instead of an array? #53
Comments
Even very large but finite arrays are expensive, and wanting to perform a task a large but finite number of times is not that unusual. I note that Python's |
I don't know why we're placing so much importance on what Python does/did. Unlike generators and iterators, this is a decidedly trivial feature that exists in many scripting languages, often as a rather pedestrian addition that does what it says on the tin: return a range of values.
I'd argue that's a non-issue, as sparse arrays with a predetermined length (i.e.,
Yes, that's why we have for(let i = 0; i < 3e7; ++i){
...
} |
It’s very easy to turn an iterator into an array - spread or Array.from. It’s mostly impossible to avoid the overhead of an array when you start with that and want an iterator. i care about python precisely zero; i think that this is what makes the most sense for JavaScript and for idiomatic JavaScript use cases. |
The motivation for this addition is literally "because we don't have it yet™". It's clear that it's intended to be a high-level, user-centric convenience, and not something to grease the wheels of web-apps to make them run faster… So it stands to reason we'd want to cut out as many keystrokes as possible for developers by returning a more commonly-used and useful object (which is already an iterator, and doesn't require an extra
Please elaborate, because I'm stretched to think of any realistic use cases where the trade-off between convenience and performance makes sense. Seriously. |
Most likely, iterators helpers will be standardized before Number.range(0, Infinity).filter(it => !(it % 17)).map(it => it ** 2).take(100).toArray() |
And also, const uppercase = Number.range(65, 91).map(n => String.fromCharCode(n)).toArray();
const lowercase = Number.range(97, 123).map(n => String.fromCharCode(n)).toArray(); |
I feel we're trying to solve two fundamentally different problems here:
My opposition to making it an iterator drops off considerably if the iterators proposal goes through, as it'd eliminate every current gripe I have with working with iterators. I was afraid bringing that proposal up would derail this discussion at best and shift goalposts at worst, since my arguments would drift from one of developer convenience to that of semantics and how overloading a utility function is poor design. |
It's only two of many use cases. But I agree that the most common subsets of the second use case can be done in a better way, for example, #48 or |
About the array-based way - see, for example, https://es.discourse.group/t/array-create/628 or https://es.discourse.group/t/provide-an-easy-way-to-create-a-new-array-filled-via-a-mapping-function/1056 - that makes sense, but it's better to implement in another proposal. |
I also don’t have any interest in infinite iterators; the use case here isn’t only to get an array of contiguous values, it’s to operate over a numeric range. One use case is to make an array, but many others aren’t. Iterator helpers certainly make this proposal much more valuable than it would be without them. |
Which you can still get with
Since you seem to be in charge of this proposal, I guess there's really nothing else to discuss here. |
On the contrary; I'm not a champion of the proposal at all, I'm just a delegate. I'm merely representing a point of view your comments claim doesn't exist - someone who only really cares about finite iterators, doesn't particularly care what other languages do, and (at least, with iterator helpers) thinks that it wouldn't make any sense at all for this to be an array, when it's so trivial to turn an iterator into an array. You're totally right that you can (ab)use an array solely to operate over a range, but that doesn't mean it's the cleanest or most intuitive or most performant way to achieve that. |
Though I believe the champion and most delegates who care about this proposal would agree it shouldn't be array, but we should also notice There is dilemma, on one side, we want good performance for large range (or even infinite, though some delegates think it's not the case, we could see a very very big range is just infinite in practice 😝 ), we want transformations via iterator helpers, on the other side, there are many use cases (if u check how |
lodash.range long predates iterators in the language. |
Yeah, but I think we still need to consider it, or we just break the use cases into two categories, and make it hard for users to adopt the feature and migrate from lodash. |
Especially with iterator helpers, i don’t think it’s much of a concern. |
When an array is what’s needed, isn’t it just a spread? I say when cause it doesn’t seem like that’s a given. Lodash-using code could already be consuming it as an iterable, and both functions do return iterables, so wouldn’t “migrating” sometimes just be identifier swaps? (assuming the same treatment of the arguments; not sure) for (let n of _.range(0, 1));
for (let n of Number.range(0, 1)); In any case, lodash has a good number of methods with the same names as methods found in ES, but very few (are there any?) which have same signatures or the same behaviors. Sometimes the ES method existed first, too (e.g. max), so it doesn’t seem like parity was a goal or expectation. |
Yeah if u always use it like
See facebook/react#20707 for more discussion. |
Yeah the official APIs could have differences, people (have to) know that. But the examples u enumerate are all edge cases. I'm not sure we can say react usage (or some similar usage rely on iterable semantics) are edge cases. |
I wouldn’t personally consider the treatment of NaN in max/max an edge case, but I did select cases that had same/similar signatures and return types deliberately to illustrate what I think is a more significant danger when folks are “migrating”: behavior differences that are only apparent for specific input values. Lodash also has same-name functions which have very different API contracts from their ES counterparts, but because those differences would generally become apparent right away if you tried to just swap em, they seem less problematic to me. “Migrations” in the first group tend to silently generate novel bugs — the sort that make it to prod. The second usually just makes bulk find-and-replace a little more tedious. I’d have expected array vs iterator to fall closer to the second of those loose categories, but I can see how it’s probably more in the middle in practice than I thought depending on usage patterns. |
@bathos I agree most of your comments. Yes, from the engineering side, subtle differences are the biggest enemy . And this is actually why I worry about exposing iterator-based api too much. The differences between one-time shot iterator and reusable iterable are subtle, and in many cases, u just use something one-time, so no difference, but code will change, and may refactor, such subtle diff are hard to discover and cause bug. And because the bug is only triggered in multi run, it's possible only occur in specific time. This is a little bit like bugs involve async order, which are the nightmare of many devs. 😂 |
What's the rationale for returning an iterator instead of a plain
Array
? Is it performance? Or is all this just so users can return an infinite range? Because the latter seems like an edge-case to me: I honestly can't foresee any realistic scenarios where one would need an infinite range that isn't part of a generator. Consider the example in the readme:README.md
example:Conversely, returning a pre-populated list of values is pretty much what I'd expect a method named
.range()
to return:It's also objectively more ergonomic, given the majority of users will probably need to convert the result to an array anyway:
The text was updated successfully, but these errors were encountered: