Why thenables?

# David Bruant (12 years ago)

The question of thenables came back on Mozilla's Bugzilla (see comment 29 & 30) with a decent share of skepticism that I share too.

I'm sorry I didn't go through all the promises discussions, but what's the rationale of supporting thenables? I fear this feature won't be necessary 2 or 3 years after native promises ship. For sure, it's of no use to those who only use native promises.

I read from the meeting notes that it was pretty much the only point of debate and a long one.

# Rick Waldron (12 years ago)

There was no long debate about thenables, only two requests for clarification of their meaning and one request for explanation of their backing store mechanism, all with immediate responses. The notes reflect exactly that.

I can't speak for Anne, with regard to comment#30, but I don't recall him sharing any kind of skepticism during the conversation. Hopefully he will clarify for us.

# Mark S. Miller (12 years ago)

On Thu, Oct 10, 2013 at 3:39 PM, Rick Waldron <waldron.rick at gmail.com>wrote:

On Thu, Oct 10, 2013 at 6:26 PM, David Bruant <bruant.d at gmail.com> wrote:

Hi,

The question of thenables came back on Mozilla's Bugzilla [1] (see comment 29 & 30) with a decent share of skepticism that I share too.

I'm sorry I didn't go through all the promises discussions, but what's the rationale of supporting thenables? I fear this feature won't be necessary 2 or 3 years after native promises ship. For sure, it's of no use to those who only use native promises.

I read from the meeting notes that it was pretty much the only point of debate and a long one.

There was no long debate about thenables, only two requests for clarification of their meaning and one request for explanation of their backing store mechanism, all with immediate responses. The notes reflect exactly that.

yes.

I can't speak for Anne, with regard to comment#30, but I don't recall him sharing any kind of skepticism during the conversation. Hopefully he will clarify for us.

Anne can correct me if I'm wrong, but I don't see any skepticism expressed in comment 30 bugzilla.mozilla.org/show_bug.cgi?id=879245#c30.

It's a reply to Jonas' 29. Interleaving the two:

Jonas: So the spec ended up with support for thenables after all? Rather

than just doing branding :(

Anne: Yes

Jonas: I take it in order to be compatible with currently existing libraries?

Anne: yes

Jonas: I guess if that's what TC39 decided on then that's what we should do. But I'm definitely saddened by it. Like you say, the past is shorter than the future.

Anne: agreed.

I would have given Jonas the same answers. We agreed to thenable assimilation for reasons that have been endlessly discussed. During the process, everyone deeply involved always wished thenable assimilation wasn't needed. But it is what we agreed to. We declared an official TC39 consensus. There are now several implementation efforts already proceeding based on that consensus -- probably many more than we know of. This is not skepticism. It is agreeing that "that's what we should do" while sharing Jonas' sadness.

# David Bruant (12 years ago)

Alright, let's do this then. Sorry for re-hashing.

Thanks for your answers,

# Ѓорѓи Ќосев (12 years ago)

I understand that adding branding to promises is impossible at this point, as it would break backward compatibility with all existing implementations.

However, thenable assimilation could still be made slightly less sad by doing something very similar to branding, without breaking backward compatibility.

The assimilation procedure could check if the thenable has a property that brands it non-assimilatable, e.g. __notPromise__ If such a property exists and is set to true, it will not assimilate it.

While far from perfect, this will at least allow objects from existing libraries that have a .then method to coexist with promises without forcing library authors (and users) to massively refactor their code. Authors would only need to add the flag __notPromise__ to their prototypes - all other existing code will continue to work normally.

I believe this would be better than globally banning such a generic name like .then as a method.

As a bonus, statis analysis tools could possibly warn users to explicitly add __notPromise__ = false; (i.e. they could advise users to explicitly express intent WRT to assimilation)

Does this sound like a good idea?

# Alex Russell (12 years ago)

On Wed, Dec 18, 2013 at 3:32 PM, Ѓорѓи Ќосев <gorgi.kosev at gmail.com> wrote:

I understand that adding branding to promises is impossible at this point, as it would break backward compatibility with all existing implementations.

That wasn't the overriding consdieration. I don't care if we don't have a-priori compatibility. The bigger issue is the lack of symbol infrastructure in the language and the inability to polyfill if we do. I've long argued for "ghetto branding" (something less foot-gun-ish than an extractable then callable), but in the interest of harmony, was not willing to fight for the better design at the end.

Our primary goal here needs to be shipping something ASAP. All other concerns are, in the scheme of things, irrelevant.'

We're already half a year late on this.

# Andrea Giammarchi (12 years ago)

Alex can I ask you if there's any specific deadline you are talking about?

Your answer sounds like "rushed is better than nothing" ... which usually might/kinda works for web-agencies but not for "would like to be corporate ready/oriented specifications", right ?

Sorry but I think that should be slightly ;-) more elaborated, if possible, thanks.

Quick One for Ѓорѓи : markdown is awesome but in this very specific ML I find easier to read **notPromise** when bold is meant, than __notPromise__ ... you know, __proto__ and sh...tuff ^_^ unless you didn't really mean __notPromise__ where in such case I think the tick should do it - sorry for the Off Topic, I was just reading through

# Alex Russell (12 years ago)

On 18 Dec 2013 18:20, "Andrea Giammarchi" <andrea.giammarchi at gmail.com> wrote:

Alex can I ask you if there's any specific deadline you are talking about?

Promises aren't important. They are a tool. And the design space is clearly overconstrained. Anyone paying attention can see that. We should have given rough consensus blessing in april and not have deprived ourselves of promises as a tool in API design for the last 7 months as a result.

Your answer sounds like "rushed is better than nothing" ... which usually might/kinda works for web-agencies but not for "would like to be corporate ready/oriented specifications", right ?

No. Not with overconstrained API that is clearly a bridge to syntax, not a final destination.

This committee has committed half a year of lost opportunities for the entire platform on the basis of pathological standards behaviour. This group has poor facility with the costs because we are far too disconnected from our users.

And no. I am not happy.

# Ѓорѓи Ќосев (12 years ago)

On 12/19/2013 02:56 AM, Alex Russell wrote:

That wasn't the overriding consdieration. I don't care if we don't have a-priori compatibility. The bigger issue is the lack of symbol infrastructure in the language and the inability to polyfill if we do. I've long argued for "ghetto branding" (something less foot-gun-ish than an extractable then callable), but in the interest of harmony, was not willing to fight for the better design at the end.

This doesn't need symbols at all, and it could be considered a "bugfix" addition - there is still time for those, I believe?

It would make .then in the method name position less of a language keyword. Without breaking compatibility with existing implementations. It will also pave the path towards a future where most implementations inherit from built in promises and those that don't set their prototype.__notPromise__ = false,

If (and when) such a future arrives, the switch can be potentially flipped to assume true instead of false, thereby essentially removing the quirk from the language - completely.

Our primary goal here needs to be shipping something ASAP. All other concerns are, in the scheme of things, irrelevant. We're already half a year late on this.

Should the language really ban all objects from using .then, forever? Should it force library authors to face the decision:

  • my objects remain incompatible with promises (cannot be returned by them), or
  • rename .then method, do a massive refactor, also force all my users to do a massive refactor

Why not provide a harmless third option?

  • add __notPromise__ flag to my prototype to tell promises not to assimilate these objects
# Alex Russell (12 years ago)

On 18 Dec 2013 20:27, "Ѓорѓи Ќосев" <gorgi.kosev at gmail.com> wrote:

  • add __notPromise__ flag to my prototype to tell promises not to assimilate these objects

That's an inverse form of "ghetto branding". But it doesn't matter either. The only thing that does is having one, standard contract -- and we are past due on that.

# Andrea Giammarchi (12 years ago)

Thanks for the clarification and FWIW, yeah ... I agree with you but I also appreciate the effort everyone is putting trying to bring new features that are as platform agnostic as possible ... but then again, we have real life/devs/code screaming for solutions ASAP

Devs also complain about mistakes though .. not sure how to win here :-(

# Ѓорѓи Ќосев (12 years ago)

On Thu 19 Dec 2013 07:42:31 AM CET, Andrea Giammarchi wrote:

The only thing that does is having one, standard contract -- and we are past due on that.

Perhaps the right path would be to try and discuss this for Promises/A++, and maybe if it happens there, ES7 afterwards :)

Devs also complain about mistakes though .. not sure how to win here :-(

No worries. I think I understand now. Still I felt it was worth a shot, although I guess I picked the wrong path (and time) to go about it.

Thanks for your time -- and sorry for creating a stir.

# Mark S. Miller (12 years ago)

I think this anti-branding idea is worth considering, but using a symbol or weakmap for the anti-branding rather than a magic double-underbar property name. Unlike prior positive thenable branding proposals, this one doesn't break existing code but still provides systems that use "then" in contrary ways an ability to cope. It also doesn't break anything premised on the promise-unwrapping consensus that we've agreed on and specced. I see only two questions:

  • Whether the benefit is worth the additional complexity?
  • If we do decide to do this, does it make sense to postpone it to ES7?

Unless there is significant demonstrated need, I am leaning away from the paying the additional complexity. OTOH, since this would mainly be about mitigating the pain of fixing contrary uses of "then", if we're going to do it at all, we should do it in ES6. If we don't, then these contrary uses of "then" will probably already be fixed by the time ES7 rolls out.

# Ѓорѓи Ќосев (12 years ago)

On Thu 19 Dec 2013 06:24:50 PM CET, Mark S. Miller wrote:

I think this anti-branding idea is worth considering, but using a symbol or weakmap for the anti-branding rather than a magic double-underbar property name. Unlike prior positive thenable branding proposals, this one doesn't break existing code but still provides systems that use "then" in contrary ways an ability to cope. It also doesn't break anything premised on the promise-unwrapping consensus that we've agreed on and specced.

Thanks :)

I see only two questions:

  • Whether the benefit is worth the additional complexity?
  • If we do decide to do this, does it make sense to postpone it to ES7?

Unless there is significant demonstrated need, I am leaning away from the paying the additional complexity.

This anti-branding proposal is not just about making the immediate future smoother. My hope is that it could potentially provide a path for the far future where research can be done to determine whether compatibility issues are still there. If they aren't, Promises could start assuming that object[notPromiseSymbol] === true, (unless explicitly stated otherwise). This practically means completely removing the wart from the language except for promise authors that don't extend the built-in Promise (or for those that actually want automatic thenable assimilation of their non-promises)

I feel that right now the then method is a bit worse than a keyword. Unlike keywords, it may cause random hard to find bugs and unpredictable behaviour (instead of syntax errors) for unsuspecting users. That makes any path that can potentially make it unreserved worth pursuing, at any time (now or in the future).

The complexity cost is not much greater than regular branding for implementers. The complexity cost is not much greater for end-users either - in both cases they will still have to know that .then is special (albiet in the latter case case, still available, and potentially not special at all in the future).

And in the latter case, cognitive load can potentially be reduced for users. Its easier to make a static analysis tool that warns them to explicitly set this[notPromiseSymbol] to either true or false when a then method is detected.

Whereas without the anti-brand, the tool would have no idea whether the user really wants automatic assimilation, or they're just unaware of the special behaviour.

OTOH, since this would mainly be about mitigating the pain of fixing contrary uses of "then", if we're going to do it at all, we should do it in ES6.

Right - thats why I tried suggesting it now... But still, I think that there are enough reasons to do this even if after the ES6 ship sails.

If we don't, then these contrary uses of "then" will probably already be fixed by the time ES7 rolls out.

I'm sorry, but I cannot accept the claim that there is such a thing as "contrary use of then". Libraries are not at fault here - ES6 is the one trampling on their use of a perfectly valid, very generic method name.

# Alex Russell (12 years ago)

On Thu, Dec 19, 2013 at 9:24 AM, Mark S. Miller <erights at google.com> wrote:

I think this anti-branding idea is worth considering, but using a symbol or weakmap for the anti-branding rather than a magic double-underbar property name. Unlike prior positive thenable branding proposals, this one doesn't break existing code but still provides systems that use "then" in contrary ways an ability to cope.

...but only in an unpolyfillable, ES6-only world.

What in the heck are we smoking if we decided that thenables were the right thing to do but we should lean on unavailable tech to do the contra? How could we POSSIBLY talk ourselves into that in good faith?

This has ventured into farce. I pray it stops.

# Gorgi Kosev (12 years ago)

...but only in an unpolyfillable, ES6-only world.

Right, they don't help existing libraries, but it would still pave a path towards branded-only promises in the (unfortunately very, very far) future (with ES5 all but gone)

Only "ghetto anti-branding" will help existing libraries (and make it possible to check if flipping the switch is okay much earlier).

Its not like combining ghetto .then with ghetto __notPromise__ is worse than just ghetto then. One thing that can be done is to make it less ugly. Since it would always be checked in tandem with then, it could simply be notConvertableToPromise. Without the double underscores, people will be slightly less likely to ask "what the hell were they smoking"

# Alex Russell (12 years ago)

Right, but number of objects you have to guard with anti-branding is much, much larger. That argues against thenables pretty strongly, but again, I don't think we need to change anything for ES6. We can repair this in ES7 if it's a problem in practice.

# Andreas Rossberg (12 years ago)

On 19 December 2013 23:29, Alex Russell <slightlyoff at google.com> wrote:

Right, but number of objects you have to guard with anti-branding is much, much larger. That argues against thenables pretty strongly, but again, I don't think we need to change anything for ES6. We can repair this in ES7 if it's a problem in practice.

I highly doubt that will be possible -- experience strongly suggests that every odd feature will be relied on in the wild by that time. If we think thenable assimilation is a problem then we have to remove it now. I, for one, would welcome that. We could still provide an explicit thenable adaptor method in the Promise API.

# Mark S. Miller (12 years ago)

Thenable assimilation is already used in the wild to a degree that we can't break. I agree with Alex that we should not re-litigate this. It is only anti-branding that could be added later -- and only if there's evidence of a need great enough to justify the added complexity. In the absence of that evidence, let's just stick to the consensus we've already achieved.

# Ѓорѓи Ќосев (12 years ago)

On 12/20/2013 02:02 PM, Andreas Rossberg wrote:

On 19 December 2013 23:29, Alex Russell <slightlyoff at google.com> wrote:

Right, but number of objects you have to guard with anti-branding is much, much larger. That argues against thenables pretty strongly, but again, I don't think we need to change anything for ES6. We can repair this in ES7 if it's a problem in practice.

Will this be shimmable (by monkey patching) in ES6 in a way that enables all promises (even those produced by the DOM) to have the new behavior?

The objects you have to guard with anti-branding are only those who have a .then method and are not promises. True, its nowere nearly good as branding. However, its still better than making ".then" a complete taboo. At least the library authors get some say on the matter.

This is how I imagine things happening:

Lets say the anti-branding property is called "convertableToPromise" and that the value "true" is assumed if the property is not defined on the thenable.

Lets just not even call it anti-branding - its just a marker.

Example 1:

A library author that is not very familiar with all the intricacies of promises writes a code parsing/transformation library that uses .then as a method of chaining different transformations. It turns out to be widely popular, and a library user wants to create a parser for remotely downloaded code. The library user fires a download operation and gets a promise, creates a transformed promise by adding a .then callback, and attempts to return the parser from within the then callback. Everything breaks.

Solutions available without anti-branding:

  • the author of the library must rename .then and must tell every single person depending on that library to refactor to the new method
  • the author declares the library incompatible with promises.

note: A lint tool cannot reliably warn them about it, because it cannot tell whether they want thenable assimilation or not (whether the class they are writing represents a "promise" or not)

Solution available with anti-branding

  • the author of the library adds Parser.prototype.convertableToPromise = false;. Everything starts working.

Even better, the library user can also likely do this.

note: A lint tool can warn them about it before the library becomes popular, avoiding the troublesome situation in the first place.

Example 2:

A library author wants to write a library for a new kind of functional asynchrony abstraction. They actually do want promises to be able to assimilate this asynchrony, so they add a method called .then. Since they are quite familiar with asynchronous code (and therefore likely with promises) they know that even though they don't have to, they can add:

Asynchrony.prototype.convertableToPromise = true;

to explicitly state that this object is infact convertable to a promise. A lint tool or an IDE can even warn them about it and they will add it to make the tool stop complaining.

Hopeful far future outcome:

ES7 arrives. Most promise libraries inherit from the built-in Promise, and the semantics of convertableToPromise are well understood by those who write async code. Therefore its possible to test whether its okay to flip the default value of thenable.convertableToPromise to false. If its determined that this would cause too much breakage at that particular moment, the decision is postponed again for ES8.

If at any point its determined that its possible to flip this switch, the "thenable wart" will be forever removed from the language.

I highly doubt that will be possible -- experience strongly suggests that every odd feature will be relied on in the wild by that time. If we think thenable assimilation is a problem then we have to remove it now. I, for one, would welcome that. We could still provide an explicit thenable adaptor method in the Promise API.

But this will make promises created by existing promise libraries incompatible with built-in promises. It will cause the same problem, but in reverse. Yes, hopefully library authors will add the branding to their promise libraries, and hopefully after a while everything will start working except legacy code, but its still a worse outcome.

That is why I proposed anti-branding - as a compromise.

# Kevin Smith (12 years ago)

I highly doubt that will be possible -- experience strongly suggests that every odd feature will be relied on in the wild by that time. If we think thenable assimilation is a problem then we have to remove it now. I, for one, would welcome that. We could still provide an explicit thenable adaptor method in the Promise API.

Agreed 100%. I'm skeptical that we've really done our homework analyzing the backward compatibility constraints for this design.

Now that we have a proper cross-realm nominal Promise type (which we didn't have before), the right thing to do is to use nominal typing. We should have started from that point and then figured out how current thenables can cope.

Too late?

Probably, but just for kicks, here's another solution:

Provide a Promise subclass, BorgPromise (Star Trek reference) which does simple thenable coercion in its then function (no funky WeakMap). All libraries which might interact with thenables switch to using BorgPromises. At some point in the future usage of non-Promise thenables will approach insignificance, at which time BorgPromise libraries can freely switch to using regular Promises, and assimilation can fade into history.

BorgPromise doesn't even have to be specified by TC39. It can be a user library:

// Remove this line sometime in the future...
import Promise from "package:borg-promise";

// package:borg-promise
class BorgPromise extends Promise {

    then(onResolve, onReject) {

        if (typeof onResolve !== "function") onResolve = x => x;

        return super.then(x => {

            // NOTE: x is a non-promise
            return isThenable(x) ?
                x.then(onResolve, onReject) :
                onResolve(x);

        }, onReject);
    }
}

export { BorgPromise as default };
# Ѓорѓи Ќосев (12 years ago)

On 12/20/2013 04:38 PM, Kevin Smith wrote:

Probably, but just for kicks, here's another solution:

What will the new DOM APIs return? Regular promises or Borg promises?

If they return regular promises, users will still need to know that they cant return $.getJSON() from within the callback of that DOM promise, because jQuery's 1.9 promise is just a thenable.

If they return Borg promises, we're back to where we started: user $.getJSON()'s source code, tries to return parser that has a .then method for transformation chaining, gets subtle errors, author of parser library can't change method because it would break existing code, lint tool can't warn author about .then because it doesn't know if the author wants thenable assimilation or not.

Also, users will have to be aware at all times what kind of promise they're interacting with.

Sorry, I don't think this is better than convertableToPromise (which is assumed to be true when undefined)

# Dean Landolt (12 years ago)

This seems like a really elegant way to avoid thenable assimilation for time immemorial.

Presumably certain promise libraries would try reset the global Promise to AssimilatingPromise (or whatever) for full parity with polyfilled environments, which would be fine. If you expect native promises, or you're not using promises at all, you can completely avoid any namespace pollution.

Another nice feature is that it's a simple opt-in rather than a much more complex opt-out approach that something like anti-branding would require.

# Kevin Smith (12 years ago)

Presumably certain promise libraries would try reset the global Promise to AssimilatingPromise (or whatever) for full parity with polyfilled environments, which would be fine.

If you're using modules, you wouldn't need to mess with the global object. You could just import AssimilatingPromise as "Promise" into the current module. The local binding would override the global Promise variable.

# Ѓорѓи Ќосев (12 years ago)

On 12/20/2013 05:13 PM, Kevin Smith wrote:

If you're using modules, you wouldn't need to mess with the global object. You could just import AssimilatingPromise as "Promise" into the current module. The local binding would override the global Promise variable.

Here is code that breaks with that solution:

function createParser(code) { return {then: chainedTransformation => { ...; return this; } } }

waitForFormSubmit()
.then(e => $.get(getUrlFromUrlField()))
.then(code => createParser(code, options))
  1. If you're just importing AssimilatingPromise, the DOM-interacting function returns a regular Promise and breaks when it gets to the unbranded jQuery $.get
  2. If you're overriding the global Promise with AssimilatingPromise, it breaks at createParser as the parser gets assimilated (infact it will go into an endless loop)
# Kevin Smith (12 years ago)

What will the new DOM APIs return? Regular promises or Borg promises?

Regular promises. Just make the application developer community aware that if you're going to use the new promise returning DOM APIs, you'll need to upgrade your libraries. Library authors need to provide versions which use standard Promises (or BorgPromises for backward compatibility), if available.

Moving forward always involves tradeoffs. Do you want a little transition pain, or a crummy legacy? Now is the only opportunity to make that choice.

I think as humans we tend to undervalue the future and overvalue current pain. The future viability and health of the platform should be the overriding concern.

Anyway...

# Dean Landolt (12 years ago)

True, though if you're using modules like that you won't need thenable assimilation anyway -- you can count on your code having native promises. If I understood Alex Russell correctly the core problem is cross-realm promise polyfillability.

It seems to me we could solve this problem by just having promise libraries monkey-patch Promise.prototype.then to assimilate. If you load a promise lib into a realm it should patch then (and then perhaps agree on some way to communicate to other promise libs not to bother patching the patch).

If there's no practical issues with collision (as advocates for assimilation claim) this is an extremely low risk proposition. But again, this would be completely opt-in so your code is only affected if you choose to load a promise lib that does this.

This solves the core issue cleanly (as put forth by Alex, at least) \without pissing in the namespace pool.

# Kevin Smith (12 years ago)

Here is code that breaks with that solution:

Not necessarily - if you're using new DOM APIs which return promises, then you need to upgrade your libraries. Libraries are easy to upgrade. A crummy legacy is not.

# Gorgi Kosev (12 years ago)

Okay, so how will end users or jQuery authors upgrade their promises to become ES6-compliant?

# Kevin Smith (12 years ago)

I don't know enough about jQuery's internals to write convincing code, but the basic idea is that you feature detect for ES6 Promises. If that test passes, then you implement jQuery.Deferred using a Promise subclass (which would do assimilation for backward compatibility reasons). Otherwise you implement it the old-fashioned way.

Application developers who are going to hook into the new DOM API's need to upgrade their jQuery.

Of course, jQuery promises will still over-assimilate objects which have a then method but aren't "thenable", but that's jQuery's backward compatibility problem, not the entire language's.

Am I missing something? I usually do... : )

# Dean Landolt (12 years ago)

I'd only point out that jQuery's Deferred API varies pretty wildly from ES6 Promises, so this could end up pretty ugly.

The alternative is to just let jQuery be jQuery and upgrade (or bastardize, depending on your perspective) ES6 Promises to assimilate. I don't know why this didn't occur to me before -- it seems pretty much ideal.

# Gorgi Kosev (12 years ago)

jQuery was just an example.

The basic idea is that you feature detect for ES6 Promises. If that test passes, then you implement jQuery.Deferred using a Promise subclass (which would do assimilation for backward compatibility reasons). Otherwise you implement it the old-fashioned way.

Hold on there. If they already do assimilation, why would you even inherit?

So you're saying that promise library authors should rearchitect their entire libraries, and that solution is better than them just adding a flag to their prototype which says "convertableToPromise = true;" ? Which can be monkey patched by end users too?

How is this even remotely practical? This is my list of options and what I think about their practicality

  • full reserving a commonplace method named "then" - impractical, error prone and unfriendly to both existing and new code (accidental then is very possible)

  • reserving the combination of "then" and "convertableToPromise" - practical, sensible tradeoff (as symbol-based branding isnt available, and accidentally adding both properties isn't very possible)

  • expecting promise libraries to upgrade by simply setting a flag convertableToPromise = true - practical (can also be monkey-patched by end-users) although painful for existing promise libraries and users at the beginning

  • expecting non-promise libraries to mark their prototypes with convertableToPromise = false (assuming true by default) - slightly less practical, but painless for existing promise libraries and users at the beginning. (The intent of course is to flip the switch later)

  • using symbols for branding/anti-branding - impractical, because it cannot be shimmed

  • expecting promise libraries to upgrade by subclassing Promise - impractical, some (like Bluebird) have complex, hand tuned performance-optimized internals which even makes them faster than the native implementation, and some have extra features which will need to be re-done in a new way. (full rearchitecting)

If you ask me, the only viable options are "ghetto branding" and "ghetto anti-branding".

# Alex Russell (12 years ago)

On Fri, Dec 20, 2013 at 5:02 AM, Andreas Rossberg <rossberg at google.com>wrote:

I highly doubt that will be possible -- experience strongly suggests that every odd feature will be relied on in the wild by that time. If we think thenable assimilation is a problem then we have to remove it now. I, for one, would welcome that. We could still provide an explicit thenable adaptor method in the Promise API.

We can create an alternative to .then() at some future point. You're right that if we continue to pile into the misbegotten .then() clown car, all is lost for all time, but I don't think that's our only hope.

Anyhow, what's done is done. Promises for ES6 are done and we need to get on with understanding our options in that world.

# Kevin Smith (12 years ago)

Hold on there. If they already do assimilation, why would you even inherit?

So that the system will see jQuery's assimilating promises as real promises.

So you're saying that promise library authors should rearchitect their entire libraries, and that solution is better than them just adding a flag to their prototype which says "convertableToPromise = true;" ? Which can be monkey patched by end users too?

I think you are probably overstating the burden on library authors in order to make your proposal look better. : )

Anyway, there's no point arguing about it. I just thought it would be fun to see what a good solution that prioritizes the future would look like. I think I've convinced myself that the backward compatibility constraint has been overestimated (probably because those working in the space also author promise libraries).

But like I said: no point in arguing over it.

# Gorgi Kosev (12 years ago)

I think you are probably overstating the burden on library authors in order to make your proposal look better. : )

I don't feel I'm overstating the burden. I thought that the pains of having to subclass to get correct interoperability behavior are well understood from experiences with other object-oriented programming languages. Thats one of the reasons for implicit interfaces...

You're right that backward compatibility has probably been overestimated. And indeed I'm biased - I am using a promise library in day-to-day coding (Bluebird) which will be impossible to re-architect by inheriting from built-in Promises. I'm using it because it gives me performance, long stack support, filtered catch i.e. .catch(predicate, handler) and other great goodies.

But after adding promises to the language, the first pains will be felt by promise users and promise library authors, as they're the most likely to adopt them quickly.

Only later when promises start becoming more and more commonplace (with more DOM APIs producing them) the pains of reserving a .then method become potentially greater. Hopefully all promise users would patch their libraries in time for ES7 by marking and it will be possible to flip the switch to the other state (start assuming convertableToPromise = false).

I honestly believe that my transitional proposal causes the smallest amount of overall pain. If I felt that another proposal is less painful I'd gladly accept it instead (e.g. I'm still not sure if starting with assuming convertableToPromise = true is better)

# Kevin Smith (12 years ago)

You're right that backward compatibility has probably been overestimated. And indeed I'm biased - I am using a promise library in day-to-day coding ([Bluebird][1]) which will be impossible to re-architect by inheriting from built-in Promises. I'm using it because it gives me performance, long stack support, filtered catch i.e. .catch(predicate, handler) and other great goodies.

I guess I don't see a reason why you can't do all of that with subclassing. The only thing subclassing is going to force you into is using the system's microtask queue. Other than that you can tweak the API all you like. It's hard to believe that a user-land library can beat the system in terms of performance and debug-ability.

# Rick Waldron (12 years ago)

On Fri, Dec 20, 2013 at 12:24 PM, Gorgi Kosev <gorgi.kosev at gmail.com> wrote:

Okay, so how will end users or jQuery authors upgrade their promises to become ES6-compliant?

Still early, but follow here: bugs.jquery.com/ticket/14510

# Gorgi Kosev (12 years ago)

I guess I don't see a reason why you can't do all of that with subclassing. The only thing subclassing is going to force you into is using the system's microtask queue. Other than that you can tweak the API all you like. It's hard to believe that a user-land library can beat the system in terms of performance and debug-ability.

Okay, lets say that this is indeed would be the ideal way to go about it. I'm far from convinced (interfaces, not subclassing is the solution here -- but not too broad of an interface), but lets just leave that aside - the fact is, I am not an implementer, Petka Antonov would know the right answer to that question.

My understanding is that its too late for that proposal, right? It has been already decided that backwards-compatibility is important. I believe you also mentioned that its "probably too late". Well, its still not too late for ghetto anti-branding. It doesn't noticeably change exiting behavior - its backwards-compatible, yet provides an "exit" for the future.

Can you leave what you feel is the best solution aside for a moment and comment on this proposal instead?

# Kevin Smith (12 years ago)

Can you leave what you feel is the best solution aside for a moment and comment on this proposal instead?

Leave aside the best solution - NEVER!!!

: )

2-year old is bugging me, but I'll respond later...

# Kevin Smith (12 years ago)

Can you leave what you feel is the best solution aside for a moment and comment on this proposal instead?

We don't want anti-branding because that puts the "burden of proof" in the wrong place. I would only consider it if (a) some kind of branding is absolutely required, and (b) there is absolutely no other option.

The problem with "branding" in general is that TC39 has decided, for better or worse, that duck-typing of this kind is going to be done with symbols. And symbols aren't really pollyfillable.

My recommendation to TC39 would be to remove thenable coercion from the Promise design and inform library authors that they need to subclass the Promise type if available. If it turns out that library authors are unable to do so, then add coercion back to the design.

This research can be done within a couple of weeks, I would think.

# Gorgi Kosev (12 years ago)

We don't want anti-branding because that puts the "burden of proof" in the wrong place. I would only consider it if (a) some kind of branding is absolutely required, and (b) there is absolutely no other option.

After thinking about it a while longer, I also realized that there would be nothing "temporary" about anti-branding. Library authors will likely have to support ES6 for quite a while, therefore after flipping the switch, everyone will be stuck doing both branding and anti-branding -- promise library authors doing the first for ES7+, others doing the second for ES6.

The problem with "branding" in general is that TC39 has decided, for better or worse, that duck-typing of this kind is going to be done with symbols. And symbols aren't really pollyfillable.

I agree - that seems to be the core issue. Why was this decided? Can someone please point me to the relevant thread(s) that I could read?

By the way, I noticed that duck typing here is already not done with symbols. So the argument isn't "its either going to be symbols or nothing". Its "its either going to be symbols or the method then". The fact that Promises already break the rule is a sign that its probably a bad decision, at least for things that benefit from polyfills. So yeah, I'd definitely like to read more.

# Gorgi Kosev (12 years ago)

We don't want anti-branding because that puts the "burden of proof" in the wrong place.

I forgot to mention this. It should be obvious that the burden of proof is already in the wrong place, and in the most hostile way possible too (If you don't want to be treated as a promise, you must not use the generic method name .then. Don't use then, or you will be assimilated. Resistance is futile).

Anti-branding is indeed crappy and hacky and not really temporary, but it does make resistance possible...

# Kevin Smith (12 years ago)

By the way, I noticed that duck typing here is already not done with symbols. So the argument isn't "its either going to be symbols or nothing". Its "its either going to be symbols or the method then".

That's right, and (digging a little deeper) there are two problems with using symbols:

  1. Given that DOM wants Promises ASAP, I'm pretty sure they don't want a dependency on ES6-only symbols.

  2. Duck-typing symbols need to work cross frame, which means either: a) we need System.then added to the spec. b) we need the cross frame symbol registration thing in place (i.e. Symbol.for("then")).

Now Promises are by design "pointer-like": featureless indirectors, if you will. So nominal (non-duck) typing should work just fine for this case. And if nominal typing works, then we can avoid these language-feature dependency problems.