StopIteration, ForwardToTarget, ... & symbols

# Herby Vojčík (7 years ago)

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals?

On the more general line, I got pretty fond of (unique) symbols, they seem like very good things for these sentinel-like objects. Is it ok / preferable to use them for that? No need to use empty objects bearing only their identity any more, imo.

# Brendan Eich (7 years ago)

Herby Vojčík wrote:

Hi,

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals? \

Why?

Let's separate the naming and unique identity concerns. Objects have unique identity, so do symbols. But if a unique identity is required without being a property key, then an object is the right answer, not a symbol. I think this applies to StopIteration.

Then there's the naming question. Should one have to use an @-name to reference a singleton object? Again I say no. APIs have public names, mostly or always. An identifier is a fine public name, better than a non-private symbol if bound in a namespace object or module -- or even if a global like JSON.

On the more general line, I got pretty fond of (unique) symbols, they seem like very good things for these sentinel-like objects. Is it ok / preferable to use them for that? No need to use empty objects bearing only their identity any more, imo.

For unique identity, why do you care between symbol and object?

For naming, see above.

# Allen Wirfs-Brock (7 years ago)

On Nov 26, 2012, at 1:11 PM, Brendan Eich wrote:

Herby Vojčík wrote:

Hi,

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals? \

Why?

Let's separate the naming and unique identity concerns. Objects have unique identity, so do symbols. But if a unique identity is required without being a property key, then an object is the right answer, not a symbol. I think this applies to StopIteration.

Then there's the naming question. Should one have to use an @-name to reference a singleton object? Again I say no. APIs have public names, mostly or always. An identifier is a fine public name, better than a non-private symbol if bound in a namespace object or module -- or even if a global like JSON.

Orthogonal issues. A symbol object object can be the value bound to a regular identifier. The @ is primarily for literal property naming syntactic contexts. EG, after a dot.

On the more general line, I got pretty fond of (unique) symbols, they seem like very good things for these sentinel-like objects. Is it ok / preferable to use them for that? No need to use empty objects bearing only their identity any more, imo.

For unique identity, why do you care between symbol and object?

A nice thing about Symbols as I've now spec'ed them in the latest draft. It that they are truly immutable, stateless, and Realm independent. They don't carry any other object references at all. Not even to their toString method. (fun things that can be done via the MOP...)

# David Bruant (7 years ago)

Le 26/11/2012 22:11, Brendan Eich a écrit :

Herby Vojčík wrote:

Hi,

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals? \

Why?

Let's separate the naming and unique identity concerns. Objects have unique identity, so do symbols. But if a unique identity is required without being a property key, then an object is the right answer, not a symbol.

I'm not sure not being a property key is that big of a constraint, so I would say that objects and symbols are both valid equally valid.

I think this applies to StopIteration.

It is suggested on the strawman that StopIteration be a constructor [1] and I support this idea for StopIteration because an iterator may have different reasons to stop and building instances with a message (or any property) attached. I'm not sure It'd be as relevant for ForwardToTarget.

David

[1] harmony:iterators

# Brendan Eich (7 years ago)

Allen Wirfs-Brock wrote:

On Nov 26, 2012, at 1:11 PM, Brendan Eich wrote:

Herby Vojčík wrote:

Hi,

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals?
Why?

Let's separate the naming and unique identity concerns. Objects have unique identity, so do symbols. But if a unique identity is required without being a property key, then an object is the right answer, not a symbol. I think this applies to StopIteration.

Then there's the naming question. Should one have to use an @-name to reference a singleton object? Again I say no. APIs have public names, mostly or always. An identifier is a fine public name, better than a non-private symbol if bound in a namespace object or module -- or even if a global like JSON.

Orthogonal issues. A symbol object object can be the value bound to a regular identifier. The @ is primarily for literal property naming syntactic contexts. EG, after a dot.

I know it's orthogonal -- please!

It was not clear which of the two (or both) Herby was raising.

On the more general line, I got pretty fond of (unique) symbols, they seem like very good things for these sentinel-like objects. Is it ok / preferable to use them for that? No need to use empty objects bearing only their identity any more, imo. For unique identity, why do you care between symbol and object?

A nice thing about Symbols as I've now spec'ed them in the latest draft. It that they are truly immutable, stateless, and Realm independent. They don't carry any other object references at all. Not even to their toString method. (fun things that can be done via the MOP...)

The realm independence is interesting. That could be compelling indeed for StopIteration, except we already proposed isStopIteration.

# Brendan Eich (7 years ago)

David Bruant wrote:

Le 26/11/2012 22:11, Brendan Eich a écrit :

Herby Vojčík wrote:

Hi,

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals? \

Why?

Let's separate the naming and unique identity concerns. Objects have unique identity, so do symbols. But if a unique identity is required without being a property key, then an object is the right answer, not a symbol. I'm not sure not being a property key is that big of a constraint, so I would say that objects and symbols are both valid equally valid.

Perhaps not, but it's odd to make a symbol just for (e.g.) immutability with no properties or [[Prototype]]. For realm-independence is more interesting. More below, on your good point I had forgotten momentarily in replying to Allen a minute ago.

I think this applies to StopIteration. It is suggested on the strawman that StopIteration be a constructor [1] and I support this idea for StopIteration because an iterator may have different reasons to stop and building instances with a message (or any property) attached.

Yes, indeed, and StopIteration as constructor is used implicitly when a generator function returns a value. This makes the case for object not symbol. isStopIteration copes with realm-dependence.

I'm not sure It'd be as relevant for ForwardToTarget.

I leave that to you and Tom :-P.

Thanks,

# Allen Wirfs-Brock (7 years ago)

On Nov 26, 2012, at 5:27 PM, Brendan Eich wrote:

David Bruant wrote:

Le 26/11/2012 22:11, Brendan Eich a écrit :

Herby Vojčík wrote:

Hi,

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals? \

Why?

Let's separate the naming and unique identity concerns. Objects have unique identity, so do symbols. But if a unique identity is required without being a property key, then an object is the right answer, not a symbol. I'm not sure not being a property key is that big of a constraint, so I would say that objects and symbols are both valid equally valid.

Perhaps not, but it's odd to make a symbol just for (e.g.) immutability with no properties or [[Prototype]]. For realm-independence is more interesting. More below, on your good point I had forgotten momentarily in replying to Allen a minute ago.

I think this applies to StopIteration. It is suggested on the strawman that StopIteration be a constructor [1] and I support this idea for StopIteration because an iterator may have different reasons to stop and building instances with a message (or any property) attached.

Yes, indeed, and StopIteration as constructor is used implicitly when a generator function returns a value. This makes the case for object not symbol. isStopIteration copes with realm-dependence.

There would be another way to accomplish supporting generator return values that doesn't require dynamically constructing new StopIteration instances and avoids all Realm issues: Capture the return value in the generator instance when it enters the "closed" state and then throw the single immutable StopIteration object (could be a Symbol). A client of the generator instance that catches StopIteration and expects a return value can directly query the generator instance (via a property access) to get that return value. It accomplishes the same thing but doesn't require multiple StopIteration instances, an extract allocation on generator returns, a call to isStopIteration to test, etc.

# Brendan Eich (7 years ago)

Allen Wirfs-Brock wrote:

There would be another way to accomplish supporting generator return values that doesn't require dynamically constructing new StopIteration instances and avoids all Realm issues: Capture the return value in the generator instance when it enters the "closed" state and then throw the single immutable StopIteration object (could be a Symbol). A client of the generator instance that catches StopIteration and expects a return value can directly query the generator instance (via a property access) to get that return value. It accomplishes the same thing but doesn't require multiple StopIteration instances, an extract allocation on generator returns, a call to isStopIteration to test, etc.

This is not the PEP-380 path we've championed, but let's talk about it. Say the generator-iterator has a .value property that contains the return value. Any for-of iteration will suppress the value, so no difference there. A task.js (taskjs.org) style scheduler, on the other hand, will have to do something like this (diff from ES6-as-proposed task.js indicated with -/+ lines):

function runScheduledTask(task) { var result = task.result, send = (task.runState === R_RESOLVED); try { task.runState = R_RUNNING; task.result = void 0; if (task.threadState === T_CANCELLED) { task.thread.close(); task.result = void 0; task.runState = R_RESOLVED; task.threadState = T_CLOSED; } else { var p = (send ? task.thread.send(result) : task.thread"throw") || READY; task.runState = R_BLOCKED; p.then(function(value) { task.result = value; task.runState = R_RESOLVED; if (task.threadState === T_STARTED) { task.scheduler.schedule(task); pump(task.scheduler); } }, function(e) { task.result = e; task.runState = R_REJECTED; if (task.threadState === T_STARTED) { task.scheduler.schedule(task); pump(task.scheduler); } }); } } catch (e) { task.threadState = T_CLOSED; if (e instanceof StopIteration) {

  •       task.result = e.value;
    
  •      task.result = task.thread.value;
           task.runState = R_RESOLVED;
           task.deferred.resolve(e.value);
       } else {
           task.result = e;
           task.runState = R_REJECTED;
           task.deferred.reject(e);
       }
    
    } }

Not much of a change, but something crucial is lost. The try could possibly .send or .next another task's generator-iterator (task.thread), possibly under pump if it immediately dispatched (it doesn't, but suppose it did). You're programming by side effects here, requiring the catcher of e to know that task.thread.value is the mutable pigeon-hole to inspect.

This smells pretty bad compared to the PEP-380 style alternative, which cleanly localizes the result to e.value.

# David Bruant (7 years ago)

Le 27/11/2012 02:27, Brendan Eich a écrit :

David Bruant wrote:

Le 26/11/2012 22:11, Brendan Eich a écrit :

Herby Vojčík wrote:

Hi,

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals? \

Why?

Let's separate the naming and unique identity concerns. Objects have unique identity, so do symbols. But if a unique identity is required without being a property key, then an object is the right answer, not a symbol. I'm not sure not being a property key is that big of a constraint, so I would say that objects and symbols are both valid equally valid.

Perhaps not, but it's odd to make a symbol just for (e.g.) immutability with no properties or [[Prototype]]. For realm-independence is more interesting.

I'm not sure I understand your reluctance to symbols. In practice, I would use "throw StopIteration" (or ForwardToTarget if such a thing comes to existence) and I couldn't care less if the value has a [[Prototype]] or not, is mutable or not, or can be used as property key or not. As a matter of fact, only the identity (or [[NativeBrand]]) of this value is used by the engine, so none of the rest matters in one direction or the other.

I have been thinking more about it, because "throw StopIteration" feels a bit like a hack in the sense that the goal is not to throw an error. Actually, it's already known in the iterator protocol that the error will be caught by the direct caller (the JS engine). What we need is a way to end a frame in a way that unambiguously signifies that the iteration stops. The Legitimate Way™ would be to add a new "stopiteration [reason];" statement to end a frame. But it doesn't scale that well (and probably isn't backward compatible). So hacking into the two ways to end a frame we currently have (return and throw) is what we use. Throwing a unique value (or a "class" of values) can be seen as an extensibility mechanism aimed at enabling user-defined frame-ending statements. Now, with things seen from this angle, an object or a symbol doesn't make much of a difference and is more of an implementation detail than anything else.

# Herby Vojčík (7 years ago)

Brendan Eich wrote:

Allen Wirfs-Brock wrote:

On Nov 26, 2012, at 1:11 PM, Brendan Eich wrote:

Herby Vojčík wrote:

Hi,

shouldn't StopIteration, ForwardToTarget from "Notification proxies" thread and similar ones be rather well-known unique symbols (like @iterator), now that we have them, instead of well-known globals?
Why?

Let's separate the naming and unique identity concerns. Objects have unique identity, so do symbols. But if a unique identity is required without being a property key, then an object is the right answer, not a symbol. I think this applies to StopIteration.

Then there's the naming question. Should one have to use an @-name to reference a singleton object? Again I say no. APIs have public names, mostly or always. An identifier is a fine public name, better than a non-private symbol if bound in a namespace object or module -- or even if a global like JSON.

Orthogonal issues. A symbol object object can be the value bound to a regular identifier. The @ is primarily for literal property naming syntactic contexts. EG, after a dot.

I know it's orthogonal -- please!

It was not clear which of the two (or both) Herby was raising.

Both, and in fact all the three (including, but not mentioning realm indepndence, which is a plus for such cases).

I felt as a good thing having specific naming, realm independence and unique identity in one language construct. That's why I naturally think of symbols as usable in such situations.

Allen wrote: "The @ is primarily for literal property naming

syntactic contexts. EG, after a dot." Primarily means, I can use it

also out of the "after the dot (including concise method names / extended literal property names)" context. Is it encouraged to use them in such a way? As a "unique, realm-independent object with @name"? Or this use case is discouraged? Or no opinion given in spec leaving the wild to find its use?

# Brendan Eich (7 years ago)

David Bruant wrote:

I'm not sure I understand your reluctance to symbols.

Because an object has unique identity, and StopIteration is a constructor, it can't be a symbol.

I have been thinking more about it, because "throw StopIteration" feels a bit like a hack in the sense that the goal is not to throw an error. Actually, it's already known in the iterator protocol that the error will be caught by the direct caller (the JS engine).

Known at runtime.

What we need is a way to end a frame in a way that unambiguously signifies that the iteration stops. The Legitimate Way™ would be to add a new "stopiteration [reason];" statement to end a frame.

I don't know what you mean. The problem is not static, however, so not local. In the case of task.js for example, the thrown StopIteration must be caught explicitly and the yield from which a resumed generator throws might be delegated (yield*).

# Brendan Eich (7 years ago)

Herby Vojčík wrote:

I felt as a good thing having specific naming, realm independence and unique identity in one language construct. That's why I naturally think of symbols as usable in such situations.

However, StopIteration in the championed proposal is a constructor too, so an object not a symbol.

# Allen Wirfs-Brock (7 years ago)

On Nov 27, 2012, at 12:45 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

There would be another way to accomplish supporting generator return values that doesn't require dynamically constructing new StopIteration instances and avoids all Realm issues: Capture the return value in the generator instance when it enters the "closed" state and then throw the single immutable StopIteration object (could be a Symbol). A client of the generator instance that catches StopIteration and expects a return value can directly query the generator instance (via a property access) to get that return value. It accomplishes the same thing but doesn't require multiple StopIteration instances, an extract allocation on generator returns, a call to isStopIteration to test, etc. ... Not much of a change, but something crucial is lost. The try could possibly .send or .next another task's generator-iterator (task.thread), possibly under pump if it immediately dispatched (it doesn't, but suppose it did). You're programming by side effects here, requiring the catcher of e to know that task.thread.value is the mutable pigeon-hole to inspect.

This smells pretty bad compared to the PEP-380 style alternative, which cleanly localizes the result to e.value.

It's always bothered me that there is do direct linkage between between a generator instance that might throw a StopIteration and the actual StopIteration value that is thrown. In particular, if you catch StopIteration you don't know which generator instance may have thrown it. I don't think this is an issue for for-of invoked generator instances but it might be an issue of mainly invoked generator instances such as those in a task scheduler. There is nothing that prevents somebody invoking next an a generator/iterator without wrapping it with a try-catch.

So here is a new idea. Why not throw the iterator/generator instance it self as the "stop iteration" exception instance. For example:

function *myGenerator() { yield 1; return 2; }

myGen = myGenerator(); try { while (true) doSomething( myGen.next() } catch (e) { if (e===myGen()) doLast(myGen.returnValue); else throw e; }

Someone calling a generator next method, by definition a a reference to the generator so has it available to look for as an exception object.

# Brendan Eich (7 years ago)

Allen Wirfs-Brock wrote:

On Nov 27, 2012, at 12:45 AM, Brendan Eich wrote:

This smells pretty bad compared to the PEP-380 style alternative, which cleanly localizes the result to e.value.

It's always bothered me that there is do direct linkage between between a generator instance that might throw a StopIteration and the actual StopIteration value that is thrown.

This is a non-issue, in JS1.7+ and Python 2.5+. Again, if for-of is driving the iteration, there's no need. If a task.js-like scheduler is doing it, then it needs to keep book or take care.

So here is a new idea. Why not throw the iterator/generator instance it self as the "stop iteration" exception instance.

No, that's ambiguous when the generator wants to throw its generator-iterator, and a capability leak in general. The generator-iterator is a capability, it should not be thrown willy-nilly to unknown catchers.

The championed proposal, based on the Pythonic design sans gratuitous GeneratorExit, is at least as well time-tested as other things we're incorporating into ES6. I don't think we should fool with it at this late stage.