<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Wed, Apr 29, 2015 at 6:32 PM, Allen Wirfs-Brock <span dir="ltr"><<a href="mailto:allen@wirfs-brock.com" target="_blank">allen@wirfs-brock.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><span class=""><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>The "another test" that you propose seems to be exactly the fix which Brandon proposed and I codified, to wit:</div><div><div><br></div></div></div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">2. If IsPromise(x) is true,<br>    a. Let constructor be the value of SpeciesConstructor(x, %Promise%)<br>    b. If SameValue(constructor, C) is true, return x.</blockquote><div class="gmail_extra"><div class="gmail_quote"><div><br></div></div></div></div></blockquote></span>That can't right.  x could be an object that is not a C but whose SpeciesConstructor is C.  In that case, a new C promise on x still has to be created.  But your code would just return x.  I would use a 'construtor' test in step 2. But if 'constructor' is not C and SpeciesConstructor is also not C then an extra level of C wrapping is needed.</div></div></blockquote><div><br></div><div>Let's call the Brendan proposal above the "early test" proposal.</div><div><br></div><div>It seems to me that if `P.@@species` is `Q` then `P.resolve` should always return an object `x` with `x.constructor==Q`, which I don't think any of the proposed tweaks yet do.  So here's that variant, which I'll call the "late test" proposal:<br></div><div><br></div><div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Promise.resolve(x)<br>1. Let C be the this value.<br>2. If Type(C) is not Object, throw a TypeError exception.<br>3. Let S be Get(C, @@species).<br>4. ReturnIfAbrupt(S).<br>5. If S is neither undefined nor null, let C be S.<br>6. If IsPromise(x) is true,<br>     a. Let constructor be the value of Get(x, "constructor").<br>     b. ReturnIfAbrupt(constructor)<br>     c. If SameValue(constructor, C) is true, return x.<br>7. Let promiseCapability be NewPromiseCapability(C).<br>8. ReturnIfAbrupt(promiseCapability).<br>9. Let resolveResult be Call(promiseCapability.[[Resolve]], undefined, «x»).<br>10. ReturnIfAbrupt(resolveResult).<br>11. Return promiseCapability.[[Promise]].</blockquote></div><div><br></div><div>This moves the test down from step 2 to step 6, and ensures that we test `x.constructor` against the same species that we would use to create the capability.  The returned promise thus always has a consistent value for its constructor property.</div><div><br></div><div>It's hard for me to determine which of these is actually preferred, although both are more consistent that the current [[PromiseConstructor]] semantics.</div><div><br></div><div>It would help if the ES2015 proposal actually had any objects where `C[Symbol.species] !== C`, but it does not appear to.  So let's look at the smalltalk usage of species.</div><div><br></div><div>To quote an arbitrarily-chosen smalltalk glossary (<a href="http://www.mimuw.edu.pl/~sl/teaching/00_01/Delfin_EC/Glossary.htm">http://www.mimuw.edu.pl/~sl/teaching/00_01/Delfin_EC/Glossary.htm</a>):<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><b>species</b>: The generic class of an object. This is normally the same as the class of an object, otherwise it is a class which describes the generic group of classes to which a particular class belongs. Note that this does not necessarily have to be class from the same branch of the hierarchy.</blockquote><div> <br></div><div>With this meaning for `@@species`, when one sets `MyPromise.constructor[Symbol.species] = Promise;`  then one is saying that even though this is a `MyPromise` it's still generically a `Promise` (of the `Promise` species).  Then the "early test" `Promise.resolve` is entirely consistent in passing through anything whose species is `Promise`.  The "late test" semantics are a bit harder to describe in terms of species (even though they seem more consistent if you are just looking at the `constructor` property of the result).</div><div><br></div><div>The smalltalk motivating example usually given for species is the equivalent of:</div><div>```<br></div><div>WeakArray.constructor[Symbol.species] = Array;</div><div>```</div><div>so that the result of `WeakArray.filter(...)` isn't itself a weak array and so won't mysteriously disappear in the hands of the caller.  Moving this example to `Promise`s we'd have:</div><div>```</div><div>WeakPromise.constructor[Symbol.species] = Promise;</div><div>```</div><div>and `WeakPromise` would hold a weak reference to some value, but it wouldn't cause the promise returned by `Promise#then` to also be a weak reference.</div><div><br></div><div>Moving from the reference domain to the time domain, we can imagine that:</div><div>```</div><div>TimeoutPromise.constructor[Symbol.species] = Promise;</div><div>```</div><div>could also be used to ensure that a given promise is rejected if not resolved within a certain time period, but the "timeout" functionality is not intended to be inherited by promises chained with `then`.</div><div><br></div><div>The "early test" and "late test" proposals give different results and interpretations for:<br></div><div>```</div><div>p = Promise.resolve(weakPromise);</div><div>p = Promise.resolve(timeoutPromise);</div><div>```</div><div>In the "early test" proposal, both the `weakPromise` and the `timeoutPromise` are both already of the `Promise` species, and so don't need to be recast.  In the "late test" proposal these would both be recast to new `Promise`s -- possibly creating a new strong reference to the value in the `weakPromise` case.</div><div><br></div><div>On the other hand, both "early test" and "late test" give awkward semantics for:</div><div>```</div><div>pp = WeakPromise.resolve(p);<br></div><div>pp = TimeoutPromise.resolve(p);</div><div>```</div><div>Presumably these invocations are intended to ensure that `pp` holds a weak reference to the value or times out, respectively, even if `p` is already a generic `Promise` ("belongs to the Promise species").</div><div><br></div><div>But both "early test" and "late test" semantics potentially return generic `Promise` objects here, although the "early test" semantics do pass through a `WeakPromise`/`TimeoutPromise` if it is passed as an argument.</div><div><br></div><div>I think what this is saying is that `Promise.resolve` is more like a constructor than an instance method, and it shouldn't consult `@@species` at all.</div><div><br></div><div>So here's one more proposal.  We'll call this the "no species" proposal:</div><div><div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Promise.resolve(x)<br>1. Let C be the this value.<br>2. If IsPromise(x) is true,<br>     a. Let constructor be the value of Get(x, "constructor").<br>     b. ReturnIfAbrupt(constructor)<br>     c. If SameValue(constructor, C) is true, return x.<br>3. Let promiseCapability be NewPromiseCapability(C).<br>4. ReturnIfAbrupt(promiseCapability).<br>5. Let resolveResult be Call(promiseCapability.[[Resolve]], undefined, «x»).<br>6. ReturnIfAbrupt(resolveResult).<br>7. Return promiseCapability.[[Promise]].</blockquote></div></div><div><br></div><div>At this point I think "no species" is the correct thing to do.  It also happens to be the simplest. If someone can come up with some alternative use cases for Promise.@@species I could perhaps be persuaded to change my mind.</div><div>  --scott</div><div><br></div></div></div></div>