-
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
Simpler solution to what Number.range aims to solve #63
Comments
Are you suggesting that users should modify objects they don’t own? Symbols don’t magically make that an OK thing to do, it just reduces the likelihood of collisions. If it’s a builtin symbol, it’s not super ergonomic or discoverable or readable imo, compared to Number.range |
Hello, your API design does not follow the JavaScript built-in API convention. We previously don't have this style of API. About the performance, I believe engines can handle it. |
Hello @ljharb, I will answer each paragraph, as quoted above, below
Modification of objects not owned ?No, nada, I do not intend to suggest any modification of objects and you may agree with me that syntaxes such as I wanted to point out aspects of the
Unreadability of defined property on
|
Hello @Jack-Works, I do not intend to go against the JavaScript API convention. I only wrote code that is easy to understand, precise, fast and extensible in the case of abstractions that conform to API standards as illustrated below
That said, I look forward to suggestions related to improvements and conformity of code from you. |
@ogbotemi-2000 Prototype pollution imo is simply not worth preventing if the cost is suffering the ergonomics loss of non-string method access for common operations - and in the case of |
Thanks @ljharb, I finally understood what you meant by not modifying objects I do not own. This is ideal but not totally possible at best because the primitive types I believe the You're right about the uncertainty as regards builtins in JavaScript but, I must say, if JavaScript has evolved and matured to the point where features such as
are available thanks to ECMAScript then, I think adding features that respect the conditions below should be done albeit sparingly.
You may add more to the list above if you resonate with me |
Those conditions are fully satisfied by static methods on the constructors. |
Additionally, if the official proposal were to adopt these, the symbols would need to have reasonable names and be placed in some namespace anyway. So you wouldn't be writing |
Jordan wasn't referring to methods on strings, he was referring to methods whose names are strings, rather than Symbols. String-named methods are much more ergonomic to use, because you get to use the |
What you say is true @tabatkins. Maybe the standards-compliant aspects of my code can be used given its brevity and conciseness in the say, quick generation of arrays that will not be modified immediately, i.e. a sequence. I believe it stands to reason that such code as mine above might be used internally albeit sparingly for performance gains and not for code portability which |
Well, if performance concerns are really that important (note: iterating array with |
Performant iterations may be achieved if the internally used object Maybe providing a callback that gets called with yielded each value may improve performance whereby the operation by the callback on each value happens immediately and not delayed in the case of
If that isn't possible then you may circumvent the overhead of using generators by using an const arrayIterator = Object.create([][Symbol&&Symbol.iterator||'values']().__proto__, {next:
{value:_=>{
/**/ }
});
//which is then refactored into a function below
Iterator.range = function(obj) {
// you can implement your trusted code to make sanity checks on obj here
this.__itr||=Object.create([][Symbol&&Symbol.iterator||'values']().__proto__, {next:
{value:_=>{
/*define your implementation here with more control over the internally used {value: Number, done: Boolean} object */
return conditionMet ? {value: undefined: done:true} : {value: value, done:false}
}}});
return this.__itr; //stored as an object property in order for it to outlive this context
} the
This has the effect of making previous variables that store a reference of the by now shared
This behavior which is due to a closure may be useful in the case of updating state with new data and making that data reflect in previous states without calling a function to do so.
|
I am closing the issue given the lull in code reviews in this thread. |
Reopened to make the closing of this issue unanimous |
I look forward to comments regarding the use of |
IMHO, Iterator.range is not the right name, as there is a BigInt and potentially a Decimal and/or BigDecimal in the future. The concept of range is tight with the type of value not Iterator per se. I can understand that the number range is special because could be used to iterate over array indices but it should not a reason to attach it on the Iterator class... |
I intended to make This decision is further based on the discussion in #17, both sides Iterator/Iterable have objections and I cannot be convinced by either side. To make this proposal happen instead of dying in the discussion, I choose to explicitly mention |
Ok I switch to the #17 to continue this discussion. |
#17 is another problem, but that is why I changed this to a polymorphic function. I don't think polymorphic function is that bad. It also can be polyfilled. |
@Jack-Works Hello, it's been quite a while.
It may be that Besides, the trouble of creating an {
next: {
value: any
done: Boolean
}
} object or otherwise is just too much for what can already be achieved through the for(entry of entries) {
} I would like to suggest that you consider
My last comment before I initially closed this thread extensively addresses how you can implement iteration functionality via the elusive Good day and well done |
Yes, this is what I intended to do.
I'm sorry I can't understand this section clearly. The
Also this part. If you can explain it more clearly it would be good! |
It will provide you the context in which you will ask the right questions. |
The image present in your message failed to load @Jack-Works Apologies for the 3-day delay, I got sidetracked.
I'd like to "see" the code for You will realize that codes may look the same but are totally different and it is the aim of this by now long-ass issue to offer a 'simpler [and faster] solution...' to what this repository aim to solve. |
I also have no idea what your proposal actually is. Your code that attempted to demonstrate this "ArrayIterator" was both too incomplete to understand, and contained a bunch of inline error-handling (like ternaries to handle the possibility of an old JS that didn't know Symbols) that obfuscated the meaningful code. It also looks like it's modifying global state so you can only have one iterator in your code at a time? |
+1, I suggest you rewrite your code, to split polyfill and user code. How do you expect to polyfill your design? How will users use it? |
Iterator.range = function(num) {
console.log(this)/*logs Iterator and not the global object or window */
/* the if condition below assigns this.breakClosure to a value of num coerced into a number using the
* "+" operator; the result of which is checked to be a truthy. If otherwise, the program exits
*/
if(!(this.breakClosure = +num)) return;
/* the assignment operation below occurs only once - when this._itr is not yet defined
* this is done so as to prevent Object.create from creating a new ArrayIterator everytime when
* arrayIterator is called
*/
this._itr||=Object.create([].values(), {next:
{value:_=>{
/* this.breakClosure below is needed here so as to update its value below by reference to the
* num argument above: if it were a variable such as num and not an object property,
* the closure in this arrow function context traps its value to the very first value of the num
* argument above
*/
/* the decrement operation on this.breakClosure makes this._itr yield
* values in a descending order when iterated over
*/
return --this.breakClosure>-1
? {value:this.breakClosure, done:false}
: {value:(this.breakClosure=void 0), done:true}
/* you are in charge of what should turn up when the iterator is exhausted
* i.e. by setting this.breakClosure = 0 or null or, in my case, undefined
* interestingly not wrapping this.breakClosure=void 0 in brackets above makes the
* JavaScript VM evaluate it as null
* (coercing an object property set to undefined to be null, I guess)
*/
}}});
return this._itr
} You may add support for As regards polyfilling,
Examples let itr = Iterator.range(5),
itr_1 = new Iterator.range(10),
itr_2 = Iterator.range(7);
itr.next() // {value: 4, done: false}
Array.from(itr) // [3, 2, 1, 0], An iterable iterator.
itr.next() // {value: undefined, done: true}
itr_1.next() // {value: 9, done: false}, due to new keyword which creates a new "this" context
itr_2.next() /* {value: undefined, done: true}, automatically reflects the state of this._itr which had been modified by itr,
may be useful in reflecting changes in some state in either vanilla JavaScript or React without having to listen to events or
call a function
*/ Why I sent incomplete code before initially closing this issue is because I want to leave its implementation details up to you devs and only discuss the core of the code. |
Hi, I read your example. I don't know why you design it in a descending way by default, it looks not easy to use when you want it to be ascending. |
Man, I put it there so that you may implement it however you wish. Descending or ascending isn't the point Differences ...
Object.defineProperty(Iterator, "range", {
configurable: true,
writable: true,
value: (start, end, option) => {
if (typeof start === "number") return new NumericRangeIterator(start, end, option, SpecValue.NumberRange)
if (typeof start === "bigint") return new NumericRangeIterator(start, end, option, SpecValue.BigIntRange)
throw new TypeError("Iterator.range only supports number and bigint.")
},
})
... Needlessly verbose and large code to implement Object.create([].values(), {
{
next:{
value:_=>{/*return {value:<Number>, done:<Boolean>} based on internal conditions */ }
}}
}) Which gives more control and code visibility than the esoteric I guess I'll leave this issue open if you fail to see the bloat in the source code of your implementation of And doing so won't make the TC39 committee see the bloat since even they apparently approved it in the first place. It has been a good run, gentlemen. |
It seems like you're focusing on the implementation of the method, which isn't all that important (as long as it's possible to implement the spec reasonably). What matters is the user-visible design and API. |
I hear you, making the source code lesser and easier will make it easier and transparent when adding improvements down the line (you do have that in mind, right?) while still maintaining the "user-visible" design and API you speak of. I won't wanna use some slow addition to ECMAScript like |
No, making the source code slightly smaller will not meaningfully impact our ability to change things down the line. It also still appears that your code is modifying global state by default if you call it without |
No, no global state modified in any way - |
You call that change slightly small? @tabatkins, Nope, it ain't at all and if it is, do explain |
...yes, |
In fact, the core of the bloat in const origNext = Object.getPrototypeOf(Object.getPrototypeOf((function* () {}).next which then gets closed over in the functions using it. origNext.call() _we will just ignore why the proposal chose an approach that further costs performance via Object.create([].values(), {next:
{value:_=>{ /*return {value: <whatever>, done:<Boolean>}*/ }
}
}) which offers lesser code, determinism and fine-grained control over how and what happens. At this point, it is as though there is a smear that covers the eyes of the delegates - @ljharb, @Jack-Works, and prevents them from seeing what is plainly a benefit. |
😂😀 That is hilarious @tabatkins, I know you mean well and perhaps you may enlighten me on how |
Iterator is a globally available object, so any mutations to it are global state. Additionally, your tone is becoming hostile. Please familiarize yourself with our Code of Conduct. |
Okay, I had it wrong then. |
There is always huge harm in mutating objects you don’t own. |
If you take a look at the spec text of If the API design does not prevent V8, SpiderMonkey, or other engines from implementing it fast, it's definitely OK to use a "slower" spec text. The spec text just writes down the expectations of the API design.
About this part, yes I changed the prototype of
By global state, we mean information can be shared between two different programs that don't have direct contact (for example, hold objects/functions created by the other side) with each other. Yes, I don't see you're exposing/modifying the global state, if I understand you correctly, you're trying to write a very loose polyfill (for example, you leaked Also, if you care about polyfill performance this much, maybe you should also not use core-js (https://github.com/zloirock/core-js/blob/bc87aaee825d8131d64608d51f9a5faf1eaa0004/packages/core-js/internals/numeric-range-iterator.js) |
The global state modification was how they directly modified let itr = Iterator.range(5),
itr_2 = Iterator.range(7);
itr.next() // {value: 4, done: false}
Array.from(itr) // [3, 2, 1, 0], An iterable iterator.
itr.next() // {value: undefined, done: true}
itr_2.next() /* {value: undefined, done: true}, automatically reflects the state of this._itr which had been modified by itr, */ |
close for now since this is not a real problem, discussions are still welcome |
Hello @Jack-Works please explain why the extra measure of offering an observable difference between the polyfill this repo is for - Also, I'd like to know why you went through the trouble of storing
|
@Jack-Works, you could have used the
{
next: {
value: _=>/* closure created here */
}
}
... I am aware that Jack-Works partly thinks so too given his applaud-worthy, non-partisan observation: "Yes, I don't see you're exposing/modifying the global state, if I understand you correctly...".
I know that seeing |
This polyfill does not care for compatibility with old browsers. It just implements the proposal in the mainline browsers.
Yes. Polyfill is an exception when it implements things correctly.
It's very clear that the core of the polyfill is
It is (not now). This proposal is intended to be shipped after https://github.com/tc39/proposal-iterator-helpers/ ships. That proposal defines If an object is available by syntax, it is also a global object, for example, the
I reviewed your polyfill again and I need to point out, that your polyfill has a very serious bug. Try this code: const x = Iterator.range(10)
const y = Iterator.range(10)
console.log(x.next().value) // 9
console.log(x.next().value) // 8
console.log(y.next().value) // 7 ?!
console.log(y.next().value) // 6 ?! You store the value on the
We won't. JavaScript spec has
const generator = (function *() {}())
const generatorNext = generator.next
const arrayIterator = [].values()
const arrayIteratorNext = arrayIterator.next
// TypeError: next method called on incompatible Array Iterator
// generatorNext.call(arrayIterator)
generatorNext.call(generator) // ok
// TypeError: next method called on incompatible Generator
// arrayIteratorNext.call(generator)
arrayIteratorNext.call(arrayIterator) // ok
|
I am aware of this behavior and I explained why that is so here: #63 (comment), the last code block in the comments offered code examples and use cases pertaining to your concern. It is not a bug, it was planned and it makes for an interesting behavior in the case of an unending iteration - you'd want only I wasn't aware that Iterator will acquire global cred further down the line, perhaps you could prepare a readily-available, short list that states the future of this proposal in order to make "contributors" write code with its evolution in mind. I agree totally with your code above though, speed is not as important as unanimity of code, I guess. I had originally sent my first code suggestions for internal implementations only: #63 (comment) hence why I used an unconventional API code style. I look forward to |
How else would someone access a builtin X.y if X isn’t a global? |
See tc39/proposal-iterator-helpers#286, they have some problems now. If they're going to rename, we'll also follow. |
@ljharb You are right. It was this phrase in @tabatkins comment '...global state...' that confused me and didn't realize that you all meant |
A non-opinionated approach to what the
Number.range
proposal aims to solve in terms ofVariations
The code above generates arrays in ascending order. Hence,
For stepped outputs
Perf tests
Using
console.time
yielded very interesting resultsThe text was updated successfully, but these errors were encountered: