archives

« Bugzilla Issues Index

#1901 — Unnecessary strictness in IteratorNext, IteratorComplete, IteratorValue


25.4.3.6 and following sections have assertions that the result from an iterator is an object.

This seems like a useless assertion to me.

We already decided that it's important for yield* to re-yield the exact same value it received, so identity for results is important.

In that regard, then, I don't see the point for restricting the type of value being yielded -- as long as you can get its "value" and "done" properties that should be good enough, and there is a built-in ToObject there that's going to hit anyway later on.


I don't quite follow why it's useless to specify that the result of "next()" must be an object. Per the "Iterator" and "ItrResult" interface (25.1.2 and 25.1.3), Iterators are expected to return objects from their "next()" method. To comply to this specification you either need to perform a type-check (as currently done) or perform a ToObject() coercion. In my opinion ToObject() does not make sense, because treating primitive wrappers as ItrResult objects rather hides user errors then helps anyone.


The current strategy is useless, because it doesn't guarantee that the result value has the properties that we want (done and value). It is meant to be a sanity check but it fails, as returning {} is an option.

To me there are two things that make sense: either

(1) specify that there will be a [[Get]] on the result value of "done" and "value" at these given points of the algorithm, and that's it; or

(2) specify that the result object is expected to be of a certain type.

(2) isn't very javascripty. Insisting that the result be an object is somewhere in between, and therefore not useful, because you don't get the guarantees of (2) and still have to deal with (1).

> hides user errors

There are enough of these already that adding a weak assertion like IsObject doesn't actually buy you much.


Option (2) is certainly not wanted. And Option (1) is almost used today, because IteratorNext() is always followed by IteratorComplete(), that means [[Get]] for "done" will always be executed. That only leaves the [[Get]] on "value" open which currently is only executed when IteratorComplete() returns false.

But apart from that issue about how exactly to comply to ItrResult, I think ToObject() should only be used when it makes sense to wrap primitives. And for iterator results, wrapping primitives does not seem useful. Just like for property descriptors (cf. 6.2.5.5 step 2) or everywhere else where type checks are performed (search for "is not Object" in the draft).


Allen, I think this situation will be rare in practice.

The low-level iteration protocol has to exist, but it's more important for it to be lean than user-friendly.

Hand-implementing this protocol is a strange thing for a user to do. ES6 provides lots of in-language support (generators, the generic Array.prototype[@@iterator], Map, Set, etc.) so that you don't have to.


I don't think we really have significant issues, here. These abstract routines are just convenience factoring for uses of interators that occur in the spec.

In particular, the assertions in IterationValue and IterationComplete, for example:
1. Assert: Type(iterResult) is Object.
2. Return the result of Get(iterResult, "value").

are there because Get (and downstream, [[Get]]) require iterResult to be an object. They don't do an internal ToObject. The assertion is essentially saying that somebody upstream must have done that.

The type object check in IteratorNext is supplying that upstream guarantee. In the spec., every use of IterationValue and IterationComplete is passed a value that has been checked for objectness by IteratorNext. If guarantees the required precondition of [[Get]]


(In reply to comment #1)
> In my opinion ToObject() does not make sense, because treating primitive
> wrappers as ItrResult objects rather hides user errors then helps anyone.

I agree with comment 1, particularly this part of it. If a generator can yield a boolean/number/string primitive value (but only those -- if ToObject were used it'd throw a TypeError for null/undefined), and primitives are permitted as generator return values, then a ToObject will just box the thing up, and <box>.done will very likely be falsy and *silently* end iteration. Much better, instead, to simply throw when a primitive is encountered, to report the error earlier.

I think the Type() != Object then throw behavior is correct and desirable.


I think things are just fine the way they are..

An object heck has to be done somewhere because we do Get's on the value.

And as I said in Comment #5 these these abstract operations are just orchestration the use of Iterators by the actual language. Nobody hand implements these or are constrained by them in their hand use of iterators.