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

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?

In contrast, I advocate the most minimal specification possible that
yields a consistent semantics. I don't believe my approach prohibits
any of your use cases and furthermore does not presume that we have
all the information regarding 1. existing use in JavaScript 2.
existing use in other programming languages and 3. future systems
designs. Additionally, a minimal specification makes promises
enormously more flexible with vanishingly small cost.

Is JavaScript the future?

> Which use cases to support are of course what we're debating here.

My platform supports all of your use cases. Your platform appears to
be advocating specifying my use cases out of existence by changing a
constant operation into a recursive operation. Are there some other
costs you haven't presented?

> But yes, I would prefer using some other mechanism to implement progress
> notifications than nested promises.

As would I; however, that does *not* support the idea that promises'
general sequencing semantics should be degraded because *one* use for
this sequencing (tracking progress) will have a special custom API.

The choice is between a simpler, more flexible, less magical, more
future-proof specification and a specification with a special-case in
resolution to try to protect *other programmers* from themselves in
the rare event that they happen to accidentally nest promises.

David

Received on Wednesday, 8 May 2013 01:05:36 UTC