Promises Consensus with /A+ terminology

# Anne van Kesteren (7 years ago)

I basically took Tab's email and rewrote the terminology. I omitted the issues for brevity. Hopefully this helps.

For the purposes of this email, a promise "fulfilling" or "rejecting" means that its resolver's fulfill() or reject() method was called, or the equivalent internal magic. "settle" means "fulfill or reject". "resolve" means "adopt or fulfill, depending on whether the value is a promise-like or not" (in other words, what the resolver's resolve() method does). "adopt" means fulfilling or rejecting with the same value as the adopted promise.

Promises have both a .then() and a .flatMap() method.

  1. p.flatMap() does "single-level" resolution:

    • whatever p settles to, gets passed to the flatMap() callbacks.
    • The callback return value must be a promise-like, which is adopted by the output promise; otherwise, the output promise rejects with a TypeError.
  2. p.then() does "recursive" resolution on the input side (per consensus following 2 TC39-meetings ago):

    • if p fulfills to a promise-like, the .then() callbacks get moved down to that promise-like until it either fulfills with a non-promise-like, or rejects.
    • Rejection calls the rejection callback without delay; no extra resolution mechanics happen here.
    • The callback return value can be a promise-like or not. If it is, the output promise adopts it; if not, the output promise fulfills it.
  3. The helper functions (Promise.every(), etc.) use .then() semantics. That is, Promise.every() will eventually fulfill to an array of non-promise-likes.

# Anne van Kesteren (7 years ago)

On Thu, Aug 1, 2013 at 5:07 PM, Anne van Kesteren <annevk at annevk.nl> wrote:

I basically took Tab's email and rewrote the terminology. I omitted the issues for brevity. Hopefully this helps.

Having done that. I wonder if we could leave the monad part out for now. As Mark pointed out in the other thread it causes a bunch of headaches to get that correct, and since we already decided (I believe) to not break with existing practice we could ship the subset that is that and figure out the superset-promise-that-works-for-monads later. That might also give us some insight into how many people will want to wrap promises to make the monad-suitable.

# Tab Atkins Jr. (7 years ago)

On Thu, Aug 1, 2013 at 9:09 AM, Anne van Kesteren <annevk at annevk.nl> wrote:

On Thu, Aug 1, 2013 at 5:07 PM, Anne van Kesteren <annevk at annevk.nl> wrote:

I basically took Tab's email and rewrote the terminology. I omitted the issues for brevity. Hopefully this helps.

Sorry, I was waiting until Mark and Domenic had finished up their terminology discussion before I did a third rewrite.

Having done that. I wonder if we could leave the monad part out for now. As Mark pointed out in the other thread it causes a bunch of headaches to get that correct, and since we already decided (I believe) to not break with existing practice we could ship the subset that is that and figure out the superset-promise-that-works-for-monads later. That might also give us some insight into how many people will want to wrap promises to make the monad-suitable.

Let's not reopen this, please. The way I've outlined things means that then()-based stuff works compatibly with the existing spec, so that's not a concern. We've already had long threads about why nested promises are useful (namely, that "promise" in practice isn't a single type - you have multiple types of promises from different promise sources, and don't always want to smash them together).

# Mark S. Miller (7 years ago)

On Thu, Aug 1, 2013 at 10:00 AM, Tab Atkins Jr. <jackalmage at gmail.com>wrote:

On Thu, Aug 1, 2013 at 9:09 AM, Anne van Kesteren <annevk at annevk.nl>

Having done that. I wonder if we could leave the monad part out for now. As Mark pointed out in the other thread it causes a bunch of headaches to get that correct, and since we already decided (I believe) to not break with existing practice we could ship the subset that is that and figure out the superset-promise-that-works-for-monads later. That might also give us some insight into how many people will want to wrap promises to make the monad-suitable.

Let's not reopen this, please.

There are three questions here, which need to be settled in roughly this chronological order:

  1. What should tc39 do quickly, to unblock the DOM's need for promises and avoid a design fork?
  2. What should DOM do quickly the tc39's quick output?
  3. Once #1 and #2 are settled, what should tc39 do for es7?

For #1 I agree with Tab. We should not reopen this issue, but should rather proceed to settle the AP2ish design quickly. Otherwise we won't settle anything quickly and DOM will go their own way.

For #2, since whatever DOM does quickly becomes a compat constraint on all future decisions, DOM should take the minimum subset of #1 which actually meets their short term needs. I leave it to those who understand those needs to argue about what these are.

For #3, we will hopefully stay upwards compat with both #1 and #2. But compat with #2 will be more pressing, as that will have been massively deployed as part of the browser.

The way I've outlined things means that then()-based stuff works compatibly with the existing spec, so that's not a concern. We've already had long threads about why nested promises are useful (namely, that "promise" in practice isn't a single type - you have multiple types of promises from different promise sources, and don't always want to smash them together).

These have been long threads so I won't rehash them either. However, I will point out that these long threads also explain why nested promises won't be useful.

# Tab Atkins Jr. (7 years ago)

On Thu, Aug 1, 2013 at 11:27 AM, Mark S. Miller <erights at google.com> wrote:

There are three questions here, which need to be settled in roughly this chronological order:

  1. What should tc39 do quickly, to unblock the DOM's need for promises and avoid a design fork?
  2. What should DOM do quickly the tc39's quick output?
  3. Once #1 and #2 are settled, what should tc39 do for es7?

For #1 I agree with Tab. We should not reopen this issue, but should rather proceed to settle the AP2ish design quickly. Otherwise we won't settle anything quickly and DOM will go their own way.

And, importantly, except for the open question of thenable/branding, our proposal is behaviorally and API-compatible with current DOM promises.

For #2, since whatever DOM does quickly becomes a compat constraint on all future decisions, DOM should take the minimum subset of #1 which actually meets their short term needs. I leave it to those who understand those needs to argue about what these are.

Unfortunately, the thenable question still impinges on the minimum subset. :/

# Anne van Kesteren (7 years ago)

On Thu, Aug 1, 2013 at 8:25 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

On Thu, Aug 1, 2013 at 11:27 AM, Mark S. Miller <erights at google.com> wrote:

For #2, since whatever DOM does quickly becomes a compat constraint on all future decisions, DOM should take the minimum subset of #1 which actually meets their short term needs. I leave it to those who understand those needs to argue about what these are.

Unfortunately, the thenable question still impinges on the minimum subset. :/

It seems there might be consensus on that now? It just seems safer and loads simpler to start out with deploying a subset that's more commonly agreed upon.

# David Bruant (7 years ago)

Le 01/08/2013 20:27, Mark S. Miller a écrit :

whatever DOM does quickly becomes a compat constraint on all future decisions

(was the comma omitted on purpose? ;-) ) This also means that if there is a delta between the agreement and the implementation, we'll have yet another de facto standard. There are two ways in which this can happen:

  • misunderstanding of intent
  • misexpression of (even perfectly-)understood intent.
  1. What should tc39 do quickly, to unblock the DOM's need for promises and avoid a design fork?

I apologize for being annoyingly insistent with this, but: tests. An unwanted de facto standard is a de facto design fork.

I believe that to a large extent, the risk of both misunderstanding of intent and misexpression understood intent would be largely reduced by a test suite.

The people who (will) implement this feature aren't necessarily the ones involved in this, sometimes very subtle, discussion. I believe fewer information would be lost if the latters formalized their agreement in tests for the formers to implement against.

[1] bugzilla.mozilla.org/show_bug.cgi?id=856410

# Brendan Eich (7 years ago)

David Bruant wrote:

I believe that to a large extent, the risk of both misunderstanding of intent and misexpression understood intent would be largely reduced by a test suite.

+∞

# Mark S. Miller (7 years ago)

A good start would be to convert promises-aplus/promises-tests to test262 form, extending test262 in the process in order to accommodate async testing. Any volunteers?

In the meantime, promises-aplus/promises-tests itself is still a good start for most purposes.

# Domenic Denicola (7 years ago)

From: Mark S. Miller [erights at google.com]

A good start would be to convert promises-aplus/promises-tests to test262 form, extending test262 in the process in order to accommodate async testing. Any volunteers?

If someone does the latter (preferably with a simple Mocha-like done() facility), I will happily do the former. I imagine there might be licensing issues with non-Ecma members; would that still be the case for code licensed under the WTFPL?

The only divergence between DOM promises and Promises/A+ so far are:

  1. The handling of non-undefined, non-function arguments, which Promises/A+ mandates must be ignored while DOM Promises mandate must throw a synchronous TypeError. (This is a spec bug; it should result in an asynchronous TypeError rejection.)

  2. DOM Promises requires onFulfilled and onRejected to be called as if they were methods of the promise itself, whereas Promises/A+ requires they be called as functions.

  3. DOM Promises mandates an infinite loop for the code const q = fulfilledPromise.then(() => fulfilledPromise), whereas Promises/A+ mandates that q be rejected with a TypeError.

  4. DOM Promises mandates an infinite loop for the code const q1 = fulfilledPromise.then(() => q2); const q2 = fulfilledPromise.then(() => q1), whereas Promises/A+ allows (but does not require) that implementations reject q1 and q2 with a TypeError.

Of these, 1 I am ambivalent on, 2 I think was a very strange mistake, and 3 and 4 feel like oversights. But none of them are a big deal.

# Mark Miller (7 years ago)

On Fri, Aug 2, 2013 at 2:28 PM, Domenic Denicola < domenic at domenicdenicola.com> wrote:

From: Mark S. Miller [erights at google.com]

A good start would be to convert promises-aplus/promises-tests to test262 form, extending test262 in the process in order to accommodate async testing. Any volunteers?

If someone does the latter (preferably with a simple Mocha-like done() facility), I will happily do the former. I imagine there might be licensing issues with non-Ecma members; would that still be the case for code licensed under the WTFPL?

Damn. I'm glad you raised this. Yes, there are issues. I don't know what they are. Would someone more knowledgeable of this minefield like to answer? Thanks.

The only divergence between DOM promises and Promises/A+ so far are:

  1. The handling of non-undefined, non-function arguments, which Promises/A+ mandates must be ignored while DOM Promises mandate must throw a synchronous TypeError. (This is a spec bug; it should result in an asynchronous TypeError rejection.)
  2. DOM Promises requires onFulfilled and onRejected to be called as if they were methods of the promise itself, whereas Promises/A+ requires they be called as functions.
  3. DOM Promises mandates an infinite loop for the code const q = fulfilledPromise.then(() => fulfilledPromise), whereas Promises/A+ mandates that q be rejected with a TypeError.
  4. DOM Promises mandates an infinite loop for the code const q1 = fulfilledPromise.then(() => q2); const q2 = fulfilledPromise.then(() => q1), whereas Promises/A+ allows (but does not require) that implementations reject q1 and q2 with a TypeError.

Of these, 1 I am ambivalent on, 2 I think was a very strange mistake, and 3 and 4 feel like oversights. But none of them are a big deal.

I think tc39 quick consensus promises should not gratuitously differ from promises/A+, i.e., they should only differ when there's a well motivated reason, as with the addition of .flatMap and the shift of recursive flattening from the output side of .then to the input side.

On #1, I like your parenthetical variant on DOM promise behavior (async rejection) better than either what promises/A+ does or what DOM currently mandates.

# Mark S. Miller (7 years ago)

On Fri, Aug 2, 2013 at 2:42 PM, Mark Miller <erights at gmail.com> wrote:

On #1, I like your parenthetical variant on DOM promise behavior (async rejection) better than either what promises/A+ does or what DOM currently mandates.

I retract this. Although I would like it better, it is not worth diverging from promises/A+ on this one. Let's stick to promises/A+ except where it really matters (.flatMap and recursive unwrapping on input side)

# Anne van Kesteren (7 years ago)

On Fri, Aug 2, 2013 at 4:58 PM, Anne van Kesteren <annevk at annevk.nl> wrote:

On Thu, Aug 1, 2013 at 8:25 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

On Thu, Aug 1, 2013 at 11:27 AM, Mark S. Miller <erights at google.com> wrote:

For #2, since whatever DOM does quickly becomes a compat constraint on all future decisions, DOM should take the minimum subset of #1 which actually meets their short term needs. I leave it to those who understand those needs to argue about what these are.

Unfortunately, the thenable question still impinges on the minimum subset. :/

It seems there might be consensus on that now? It just seems safer and loads simpler to start out with deploying a subset that's more commonly agreed upon.

This remained unanswered for some reason. Promises are spreading like wildfire through Gecko and B2G now. Chrome is implementing them. Getting the details nailed down, or at least agreement on the /A+ subset, and then record them in dom.spec.whatwg.org as a start would be great.