Deprecating Future's .then()

Mark S. Miller erights at google.com
Tue Jun 4 08:51:53 PDT 2013


[-public-script-coord, -www-dom]


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

> [+es-discuss]
>
> 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 at google.com> wrote:
>
>> On Tue, Jun 4, 2013 at 7:34 AM, Domenic Denicola <
>> domenic at domenicdenicola.com> wrote:
>>
>>> On Tue, Jun 4, 2013 at 9:48 AM, Anne van Kesteren <annevk at annevk.nl>
>>> wrote:
>>>
>>> > On Tue, Jun 4, 2013 at 8:55 AM, Sam Tobin-Hochstadt <samth at 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.
>>
>

I am making here only an argument that .then's result behavior should be
flatMap-like rather than .map-like. As for which of these the .chain camp
prefers for .chain's result behavior, I am neutral. But if they choose
.map-like, they'd be able to avoid the ugly assimilation hack.


-- 
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130604/99a7140c/attachment.html>


More information about the es-discuss mailing list