00:17
<bakkot>
why aren't generator functions iterable?
00:17
<bakkot>
you have to manually call a generator
00:17
<bakkot>
the result is iterable, but... [Symbol.iterator] is already a call
00:18
<bakkot>
I don't see any reason why function* f(){}; for (a of f) { /* ... */ } couldn't work
00:19
<bakkot>
(function* (){}).__proto__[Symbol.iterator] = function(){ return this(); };
function* f(){ yield 0; yield 1; }
for (let x of f) console.log(x);
01:56
<Mathieu Hofman>
My generators often take arguments. Curious what your use case is where it doesn't
02:05
<Meghan Denny>
the call makes it usable more than once
02:06
<Meghan Denny>
x[Symbol.iterator]() or for (const a of f()) makes a usable Iterator
02:08
<Meghan Denny>
Mathieu Hofman: document.querySelectorAll()[Symbol.iterator]().toArray() vs [...document.querySelectorAll()] is the main one i can think of, i wonder how comparable the perf is of these variations
02:24
<Mathieu Hofman>
the call makes it usable more than once
I'm confused. A generator call result is an iterable iterator. It's not usable more than once.
02:25
<Mathieu Hofman>
I think it would be equivalent of saying [...someGenerator] should work, which I'd find confusing.
02:27
<Mathieu Hofman>
Now what I don't know is if the Iterable iterator result returned by generator calls contain all the iterator helpers. Aka if you can do someGenerator().toArray(). You likely should be able to.
02:30
<Mathieu Hofman>
I just checked, iterator helpers are available on generator results
02:31
<Mathieu Hofman>
It's just that document.querySelectorAll() returns a NodeList, which is an iterable, not an iterator
02:38
<Mathieu Hofman>
Ok I think I see what the ask is. Is it to be able to have a value that is iterable multiple times and backed by a generator? In which case I suppose you can do {[Symbol.iterator]: function *() { yield 1; yield 2; } ? I just don't really want to encourage a value that's both a callable and an iterable, that feels confusing.
02:39
<bakkot>
I just sometimes have 0-arity generators, and am annoyed that I need two calls to use them when one call would do just fine
02:40
<bakkot>
the main reason that this actually matters is that if some API takes an iterable, I have to pass it the opened generator, and the API might not close it
02:41
<bakkot>
it would be better if the API opened it itself, so that it would be responsible for closing it
02:42
<bakkot>
but we only have a built-in notion of opening iterables, not generators, and generators are not iterables so it doesn't apply
02:43
<bakkot>
I would also accept a Generator.prototype.bindGenerator which gave you a bound generator object which had [Symbol.iterator], I guess
02:47
<bakkot>

concretely: how do you correctly pass a generator to (e.g.)

function take(n, iterable) {
  if (typeof n !== 'number' || n < 0) throw new TypeError;
  let result = [];
  for (let item of iterable) {
    if (result.length >= n) break;
    result.push(item);
  }
  return result;  
}

?

Just doing take(n, gen()) is wrong: if you do that the generator will never get closed if n is invalid. So... take(n, { [Symbol.iterator]: () => gen() }), I guess? that's dumb.

03:20
<Mathieu Hofman>
Afaik a generator starts suspended so really there isn't any harm in dropping it before the first `.next()` call, right (well except for the weird sync arguments processing)
03:21
<Mathieu Hofman>
That said I do see the use case now. A "bind to use once iterable" might be a solution.
03:24
<Mathieu Hofman>
It's not obvious that such an iterable should support multiple iterations unless explicitly allowed.
06:28
<Ashley Claymore>
how many things do we think could break if the spec changed it so generator argument processing was also deferred?
09:46
<nicolo-ribaudo>

I notice a mention of a JavaScriptCore bug at https://github.com/lydell/js-tokens?tab=readme-ov-file#safari-warning, and I wonder if it's even possible to test it in test262.

When running let res = /(#)(?:a|b)+/.exec("#" + "a".repeat(1e7)); print(res?.length),

  • V8 and SM throw an error (maximum call stack exceeded)
  • XS correctly prints 2
  • JSC incorrectly prints undefined

It is possible to test something in test262 that in practice in half of the engines throws due to the call stack size?

14:17
<Richard Gibson>

I would say that it's practical with try..catch that admits only success or RangeError, but also unnecessary here if the scale is reduced a bit:

$ eshost -sx '
  let res = /(#)(?:a|b)+/.exec("#" + "a".repeat(1e6)); 
  if (Array.isArray(res)) {
    print(JSON.stringify(res.map(s => s.length > 9 ? s.slice(0, 3) + "…" + s.slice(-3) : s)));
  } else {
    print(res);
  }
'
#### engine262

RangeError: Maximum call stack size exceeded

#### GraalJS, Hermes, Moddable XS, QuickJS, SpiderMonkey, V8
["#aa…aaa","#"]

#### JavaScriptCore
null
14:26
<nicolo-ribaudo>
Oh thanks, I'll open a PR with that