W3C home > Mailing lists > Public > www-dom@w3.org > April to June 2013

Re: Deprecating Future's .then()

From: Mark S. Miller <erights@google.com>
Date: Tue, 4 Jun 2013 08:32:31 -0700
Message-ID: <CABHxS9iSn0-A69nzcuKKnhpUrMpW-Bobbb_Lu=XS+2DYVJY+7g@mail.gmail.com>
To: Domenic Denicola <domenic@domenicdenicola.com>
Cc: Anne van Kesteren <annevk@annevk.nl>, Sam Tobin-Hochstadt <samth@ccs.neu.edu>, "Tab Atkins Jr." <jackalmage@gmail.com>, Sean Hogan <shogun70@westnet.com.au>, "www-dom@w3.org" <www-dom@w3.org>, "public-script-coord@w3.org" <public-script-coord@w3.org>, Alex Russell <slightlyoff@google.com>
On Tue, Jun 4, 2013 at 7:34 AM, Domenic Denicola <
domenic@domenicdenicola.com> wrote:

> On Tue, Jun 4, 2013 at 9:48 AM, Anne van Kesteren <annevk@annevk.nl>
> wrote:
> > On Tue, Jun 4, 2013 at 8:55 AM, Sam Tobin-Hochstadt <samth@ccs.neu.edu>
> wrote:
> >> Thinking about this more, I'm now unsure why both `fulfill` and
> >> `resolve` are needed given the semantics of `.chain()` and `.then()`
> >> described below.
> >>
> >> In particular, if `.then()` chains recursively *before* calling the
> >> callback, then there's no difference between:
> >>
> >>     Future.resolve(x).then(v => ...)
> >>
> >> and
> >>
> >>     Future.fulfill(x).then(v => ...)
> >>
> >> even when `x` is a promise.  The only way to observe this is with
> `.chain()`.
> >>
> >> Thoughts?
> >
> > I'm just going to try to repeat what you said here to make sure I
> understand.
> >
> > Promise.resolve(val) creates a promise of val, regardless of whether
> > val is a promise, has a callable then property, or anything like that.
> > (In that sense it is equivalent to Future.accept() today.)
> >
> > promise.then() keeps unwrapping promise's internal value until it no
> > longer has a callable then property at which point it invokes the
> > relevant callback passed to promise.then(). (Exact algorithm TBD after
> > broader agreement.)
> >
> > promise.chain() invokes its relevant callback with promise's internal
> value.
> >
> > promise.then() and promise.chain() return value (newPromise) is
> > resolved with the return value of their callbacks after it has been
> > unwrapped once.
> In general, this approach is extremely interesting. The shift from
> focusing on promise fulfillment and being in the three states of pending,
> fulfilled, and rejected to focusing on promise resolution and being in the
> two "fates" of unresolved and resolved is a big difference. But it is
> probably a win as it ends up eliminating the state concept almost entirely,
> making it just an emergent way of describing what happens with `then`.

Agreed. This is a great direction. Thanks, Sam!

Given this direction, I think the one operation that serves as both
Promise.resolve and Promise.fulfill should be the previously suggested

> One point I am not entirely clear on is why there is *any* unwrapping of
> return values, as in the last step Anne describes. For consumption with
> `then` it seems to make no difference. I assume this is to match flatMap or
> bind semantics from other languages, so that if you use `chain` exclusively
> you match Haskell/Scala/et al. semantics?

If you don't unwrap[1] at all, i.e., go with a .map-like treatment of
return values, rather than a .flatMap-like treatment, the difference is
unobservable to those who use .then exclusively, and the semantics seems
simpler, so this would seem to be a win. But the storage costs would be *
*HUGE**! Since the implementation can't in general tell whether a promise
will be observed with .chain or .then later, it would have to preserve each
level of nesting for .then calls nested in .then calls. This would lose the
flattening property that corresponds to being insensitive to how many
levels deep in a function call chain a value was returned from. The normal
tail-recursive promise loop pattern <
would need to accumulate a level of nesting per iteration of the loop.

> I also think the name "chain" is pretty confusing, especially in light of
> already-existing terminology around promise chaining [1]. Is there
> precedence for it in other languages? flatMap seems clearest to me so far.
> [1]: https://www.google.com/search?q=promise+chaining

I agree. This terminology will lead to confusion: "To do promise chaining,
use .then. The .chain method doesn't support promise chaining." As for
.flatMap, I am indifferent, since I'm planning to avoid it myself. I leave
it to its advocates to suggest something unconfusing.

[1] I am always worried though when people use the term "unwrapping" as I
don't know what they mean. Does this mean flattening, assimilating, both,
or something else? What I mean here is to so one level of flattening. As
for whether .then should also do one level of assimilation if it sees a
non-promise thenable, I could go either way, but prefer that it should not.
The promise-cross-thenable case should be sufficiently rare that the cost
of the extra bookkeeping should be negligible.

Received on Tuesday, 4 June 2013 15:32:59 UTC

This archive was generated by hypermail 2.3.1 : Tuesday, 20 October 2015 10:46:20 UTC