5 ms·
One thing I dislike about the iterator protocol is that it creates garbage around every value you're iterating over: let set = new Set([1,2,3]), it = set.val
by anonytrary 8y ago
One thing I dislike about the iterator protocol is that it creates garbage around every value you're iterating over:
let set = new Set([1,2,3]), it = set.values(), n;
while(!(n = it.next()).done) console.log(n);
// {value: 1, done: false}
// {value: 2, done: false}
// {value: 3, done: false}
I found this strange considering javascript has symbols now. Symbols would be perfect for the iterator protocol:
while((n = it.next()) !== Symbol.iteratorDone) console.log(n)
// 1
// 2
// 3
Not that this is too much of an issue, I'm sure using yield/yield*/of will eventually optimize this.
- duckerude 8y agoThat would turn Symbol.iteratorDone into a dangerous value, that can't safely be yielded from a generator, or included in an array, or passed to a variadic function. I don't know if that would be much of a problem in practice but it feels wrong instinctively.
- tonyg 8y agoExactly. `Symbol.iteratorDone` would be a kind of "null" value. The existing approach is equivalent to returning Either<a,b> in a language with labelled sums; it carefully distinguishes between the envelope and the payload, if you like.
- anonytrary 8y agoThese are all great points. I didn't immediately consider: [1,2, Symbol.iteratorDone, 3] which would definitely break on something like that. It would seem the only real way to fix this is with a distinct wrapper.
- duckerude 8y agoPython's solution is to throw a StopIteration exception when the iterator runs out. That works out ok as long as you don't manually throw it in weird places, but a naive implementation could have high overhead because exceptions are expensive. PHP's iterators have separate methods for advancing to the next element, checking if there's a current element, and getting the current element. That's cumbersome, and requires keeping extra state, but it's abstracted over so it doesn't typically matter. I think it mimics a bizarre old way of iterating through arrays. There are multiple ways to signal the end. Javascript's is pretty clean.
- anonytrary 8y agoOof, I'd definitely take JavaScript's wrapper objects over an exception. Iteration complete isn't something that strikes me as an error, it's just the end of the finite list you're iterating over! Does the program expect the list to never end or something? Errors should be reserved for the unexpected. A list ending is very expected, unless you're dealing with an infinite generator.
- duckerude 8y agoPython uses exceptions more casually than many other languages. It follows EAFP. Exceptions don't have to be very exceptional. Doing it like that would be out of place in many languages with different sensibilities, but it fits ok in Python. It also helps that you rarely deal with StopIteration exceptions directly or even have to remember they're used under the hood.
- jerf 8y agoIt seems like that would be low-hanging fruit for a JIT. The JS layer may present it as an object but it would be easy to compile it into something else under the hood. Especially if you never touch the "done" value inside the loop, which seems like a pretty common use case.
- Klathmon 8y agoThe generational garbage collectors in all (or the vast majority of) javascript implementations is specifically tuned to make this a pretty big non-issue for the extreme vast majority of code. It's the same argument people make against many React patterns, that inline objects or inline functions generate additional garbage, but when tested it's found that the time spent cleaning up that garbage is insignificant (and in the React case, workarounds often end up causing performance issues at boot time by trying to move that garbage-generating code out of the render function where it's a non-issue!) Without measurements you are just guessing, and JITs don't always work the way most will intuitively assume they work. I've tested the overhead of creating and throwing away objects on each iteration of a loop, and the reality is that it just doesn't impact the performance of the code in any meaningful way. The GC happily cleans it up extremely fast, and in some cases the JIT will even compile the code in a way to avoid generating the garbage at all.
- fro0116 8y agoAgreed on all points, but I'd just like to point out the main reason inline objects and inline functions is a perf antipattern in React is not garbage collection pressure, but rather the fact that they invalidate perf optimizations like PureComponent and React.memo because the objects/functions will have new references on each render. If you use those two together, then you're incurring the cost of individual prop comparisons used by PureComponent/React.memo without reaping the benefit of being able to skip the render entirely if no references have changed.
- Klathmon 8y agoThey are in that one case, but the vast majority of components aren't PureComponent, and therefore it's not a universal "perf antipattern". It's also one of those things that people tend to extrapolate out. They hear that the function changing every render causes perf issues, and then just assume it's truth and apply that thinking everywhere. It's so common that the Hooks FAQ actually has a section dedicated to talking about this specifically [1]! We are getting off topic now, but it was one of the areas that I believe the react team wasn't happy about when it comes to the design of React. You had/have to treat components differently depending on whether they are pure or implement `shouldComponentUpdate` and how they implement it. It's also one of the things that the new Hooks API solves. With hooks like `useMemo` and `useCallback` you can cache those values and functions that you pass around. [1] https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-because-of-creating-functions-in-render https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-becau...
- snek 8y agoFor more info on how this isn't actually that much of a problem even without JIT: https://en.wikipedia.org/wiki/Tracing_garbage_collection#Generational_GC_(ephemeral_GC) https://en.wikipedia.org/wiki/Tracing_garbage_collection#Gen...