W3C home > Mailing lists > Public > public-script-coord@w3.org > April to June 2013

Re: Deprecating Future's .then()

From: Mark S. Miller <erights@google.com>
Date: Tue, 4 Jun 2013 08:42:45 -0700
Message-ID: <CABHxS9jUz52U9BrsTdYoVvssBRjAouhabeHQj0UJKKv+jTrwDA@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>, es-discuss <es-discuss@mozilla.org>

I just realized that this thread has occurred so far only on the wrong
lists. Please let's proceed from here only on es-discuss. This is a
language issue, not a browser issue. Let's please stop splitting the
discussion between two communities.

On Tue, Jun 4, 2013 at 8:32 AM, Mark S. Miller <erights@google.com> wrote:

> 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
> Promise.of.
>> 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 <
> http://wiki.ecmascript.org/doku.php?id=strawman:async_functions#reference_implementation>
> 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.
> --
>     Cheers,
>     --MarkM

Received on Tuesday, 4 June 2013 15:43:17 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 17:14:13 UTC