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

Re: Promises: Auto-assimilating thenables returned by .then() callbacks: yay/nay?

From: David Sheets <kosmo.zb@gmail.com>
Date: Wed, 8 May 2013 13:02:14 +0100
Message-ID: <CAAWM5Ty+TD3Gc9wg0hMmgChCRTZaP0OLdJwpJrYh7PTayfzCXQ@mail.gmail.com>
To: Jonas Sicking <jonas@sicking.cc>
Cc: Sam Tobin-Hochstadt <samth@ccs.neu.edu>, "Tab Atkins, Jr." <jackalmage@gmail.com>, Domenic Denicola <domenic@domenicdenicola.com>, "public-script-coord@w3.org" <public-script-coord@w3.org>, "Mark S. Miller" <erights@google.com>
On Wed, May 8, 2013 at 7:00 AM, Jonas Sicking <jonas@sicking.cc> wrote:
> On Tue, May 7, 2013 at 6:05 PM, David Sheets <kosmo.zb@gmail.com> wrote:
>> On Wed, May 8, 2013 at 12:38 AM, Jonas Sicking <jonas@sicking.cc> wrote:
>>> On Tue, May 7, 2013 at 3:16 PM, David Sheets <kosmo.zb@gmail.com> wrote:
>>>> On Tue, May 7, 2013 at 11:03 PM, Jonas Sicking <jonas@sicking.cc> wrote:
>>>>> On Mon, May 6, 2013 at 10:40 AM, Sam Tobin-Hochstadt <samth@ccs.neu.edu> wrote:
>>>>>> On Mon, May 6, 2013 at 1:25 PM, Jonas Sicking <jonas@sicking.cc> wrote:
>>>>>>> On Fri, May 3, 2013 at 7:10 PM, Sam Tobin-Hochstadt <samth@ccs.neu.edu> wrote:
>>>>>>>> On Fri, May 3, 2013 at 7:17 PM, Jonas Sicking <jonas@sicking.cc> wrote:
>>>>>>>>>
>>>>>>>>> Third, there's the question of what a nested promise actually means.
>>>>>>>>> Normally a promise represents "a value after some time". But what does
>>>>>>>>> a nested promise mean? "A value after a longer time" obviously isn't
>>>>>>>>> correct since there are no time constraints associated with a promise.
>>>>>>>>> "A promise after some time" also isn't really meaningful given that
>>>>>>>>> that simply means "A value after some time after some time".
>>>>>>>>
>>>>>>>> I've been staying out of all of the promises discussions, but this
>>>>>>>> just doesn't make any sense at all.  A function of no arguments is a
>>>>>>>> "computation that produces a value".  By a similar argument to yours,
>>>>>>>> functions of no arguments that produce the same aren't meaningful,
>>>>>>>> because it's a "computation that produces a computation that produces
>>>>>>>> a value".  I hope we can all see that this in fact makes perfect
>>>>>>>> sense.
>>>>>>>
>>>>>>> The difference is that it's meaningful for a function that takes no
>>>>>>> argument to return a function that takes no argument. That provides
>>>>>>> the capability for the caller of the initial function to determine
>>>>>>> when the returned function should be called. Thus it lets the caller
>>>>>>> postpone the CPU cycles spent by the returned function until the
>>>>>>> result is actually needed.
>>>>>>>
>>>>>>> To put it another way a "computation that produces a computation that
>>>>>>> produces a value" is meaningful since you can choose to not perform
>>>>>>> the second computation if it turns out that you don't need it.
>>>>>>
>>>>>> This is silly in just the same way.  Given a promise, we can choose to
>>>>>> wait for its result, or not, just as with a function, we can run it or
>>>>>> not.
>>>>>
>>>>> No. Existing promise libraries are built around the idea that the
>>>>> calculation to create the value always happens, no matter if someone
>>>>> has said they are listening to the promise or not.
>>>>
>>>> Could you please explain which part of the promise concept makes this
>>>> eager evaluation necessary?
>>>
>>> The promise concept doesn't. But I've not seen any libraries that
>>> permit it. I.e. I haven't seen any libraries that allow the creator of
>>> the promise to be notified when someone registers to be notified about
>>> the value.
>>
>> The promise can side-effect and that is how an observer will be notified.
>> No additional API is required -- merely a different type of promise
>> that obeys the same contract.
>>
>>>>> So they do not provide the ability to postpone a calculation until
>>>>> someone that has access to the promise requests the calculation to be
>>>>> performed.
>>>>
>>>> I have a promise library on my disk that does provide this ability. It
>>>> is a trivial extension.
>>>
>>> The question isn't what can be implemented. The question is what
>>> existing libraries and code does as that is an indication of what
>>> people have requested.
>>
>> Not many people thunk
>> <http://en.wikipedia.org/wiki/Thunk_%28functional_programming%29>.
>>
>> Erm, I guess except people who coded in Algol 60 and pretty much any
>> language since with first-class functions...
>>
>>>>>>> Nor have anyone brought forward any use cases.
>>>>>>
>>>>>> Consider a hash table that keeps some state remotely. Then a promise
>>>>>> is useful for the results.  If promises for promises are impossible,
>>>>>> can you store promises in the table?
>>>>>
>>>>> This is similar to the example I brought up, except that you store the
>>>>> values in a hash table rather than storing them in a database.
>>>>>
>>>>> This is indeed an interesting use case. One relevant question is: What
>>>>> is the use case for getting the promise stored in the hash/database,
>>>>> rather than getting the value that the promise represents?
>>>>>
>>>>> In your hash table example as a consumer I would think it's great if
>>>>> what I get out of the API is values rather than promises so that I
>>>>> don't have to bother with additional (recursive?) calls of .then().
>>>>>
>>>>> One reason that was brought up was that if getting the promise from
>>>>> the hash is fast, but getting the value from the promise is slow, then
>>>>> the caller might want to know about the intermediate step of having
>>>>> the promise from the hash.
>>>>>
>>>>> So essentially this is a way of modelling progress by having several
>>>>> nested promises. Each unwrapping callback represents an indication of
>>>>> progress.
>>>>>
>>>>> I'm not sure if this is a great way of modelling progress though. The
>>>>> ProgressFuture that has been proposed seems like a more robust
>>>>> solution to me.
>>>>
>>>> If you're happy with ProgressFuture, that's great. If someone else
>>>> wants to use nested promises to achieve this, will you tell them "you
>>>> can't, that's not 'robust'"?
>>>
>>> If we're creating a standardized API, we have to create a limited
>>> feature set.
>>
>> Great! Limit your features to the minimal necessary including not
>> disallowing compatible promises, not overspecifying evaluation order
>> and not breaking encapsulation through forced calculation of a
>> promise's fixpoint.
>>
>>> We can't support everyone's use cases and everyone's
>>> requirements. Those are the breaks.
>>
>> Supporting everyone's desires /ever/ is clearly unachievable in the
>> general case. Luckily, our problem is much simpler than the general
>> case.
>>
>> The present concern regards the tension between:
>>
>> 1. Precisely who gets to build compatible promises
>> 2. How much of their behavior is mandated
>> 3. How general-purpose they are
>>
>> If I understand correctly, you advocate decreeing at least: (2)
>> specific unnecessary details of evaluation and (3) limitations on
>> their general-purpose usage. To enforce these decrees, you will
>> consequently need to (1) limit who gets to write compatible promise
>> implementations. Your approach prohibits some use cases for, from what
>> I can tell, 2 primary reasons: 1. if you intentionally use them and
>> don't know what you're doing, they can be confusing and 2. you haven't
>> seen JavaScript promise library users using them. Is this correct?
>> Have I misrepresented your argument?
>
> I confess that I don't follow what you are saying above at all.
>
> What I'm trying to do is to define a API exposed to javascript code
> running in the web platform. I do not intend to define what every
> promise library must do. Anyone writing a library is free to do
> whatever they want to do.

If they are writing a compatible library, they will have to play by at
least some of the rules mandated by the API.

For example:

* You must brand a promise.
* You must include a "then" member.
* You must subclass a specific class.

> But yes, I definitely think we should precisely define how that API
> behaves, which by extension defines how you must write code to be
> compatible with that API. Generally in order to make things
> interoperable between implementations that means that we much very
> tightly mandate a particular behavior.

I think I see the disconnect now.

I absolutely think that a particular *instance* of promises should be
tightly specced.
I absolutely don't think that the protocol for *all* promises should
be based on a subset of the behavior of that instance.

That is, the specification task has two separate subtasks:

1. Specify a protocol for "promise-like" or "promise-compatible" objects
2. Specify a specific subset of this protocol that other APIs *return*

These are two separate concerns and the design space between them is
quite large. By mandating that promise resolution is performed
recursively, you take a "helpful feature" of 2. and enforce it for
every implementation of 1. This drastically limits the design space.

I think looking at this problem in terms of object protocols is
helpful as it easily suggests the distinction between the class of
semantics and a specific semantic. You can view 1. as being liberal in
what you accept and 2. as being conservative in what you emit. This
gives us room to grow and evolve.

Hope this clears some things up.

Regards,

David
Received on Wednesday, 8 May 2013 12:02:45 UTC

This archive was generated by hypermail 2.3.1 : Tuesday, 6 January 2015 21:37:49 UTC