I think we see a correlation -- not a 1.0 correlation, but something. Those who've actually used promise libraries with this flattening property find it pleasant. Those who come from either a statically typed or monadic perspective, or have had no experience with flattening promises, generally think they shouldn't flatten. Even if this correlation is there and even if it indicates what it seems to indicate, by itself it is hard to use as a basis for argument. It would merely shut out of the argument people without that experience, which is not what I intend and would not help us move forward.
At <http://research.google.com/pubs/pub40673.html>, Tom, Bill Tulloh, and I wrote a paper where we demonstrate with a *very small* example, how beautiful programming with promises can be. This example is small enough that those who advocate different rules can try rewriting it using promises-as-they-wish-them-to-be and compare. Even this exercise though would leave out the history of the code in the paper, and so not illuminate the refactoring point David just made. A very concrete example of the effect David is talking about is seen in Figure 1: The Mint Maker, on page 13. In the current paper, its deposit method reads: deposit: (amount, srcP) => Q(srcP).then(src => { Nat(balance + amount); m.get(src)(Nat(amount)); balance += amount; }) where the body of the deposit method doesn't fire until the promise for the source purse, srcP, resolves to a source purse. Previously, it read deposit: (amount, src) => { Nat(balance + amount); m.get(src)(Nat(amount)); balance += amount; } requiring its clients to pass in a resolved source purse, rather than a promise for one. Some of the clients already had a resolved source purse to pass, so for these it was no problem, they just passed it. On page 12: var ackP = paymentP ! deposit(10, myPurse); Others had received a promise for a payment purse from elsewhere, that they intended to use as a source purse. They had to do a .then on this payment purse and then send .deposit only from inside the body of the then. The buy method of page 13 used to be: buy: (desc, paymentP) => Q(paymentP).then(payment => { // do whatever with desc, look up $10 price return (myPurse ! deposit(10, payment)).then(_ => good); }) This also came up in several places in the escrow exchange agent in Figure 2. The deposit method itself, having had no explicit return, would terminate either by returning undefined, indicating success, or throwing, indicating failure. The promise for the eventual result of the deposit method, such as ackP above, would thereby be a promise-for-undefined. It would eventually either fulfill successfully with undefined or be rejected with the thrown error as the reason. The refactoring of putting the "Q(srcP).then" in the deposit method unburdened all clients such as the buy method above from doing this postponement themselves. The new buy method on page 13 now reads: buy: (desc, paymentP) => { // do whatever with desc, look up $10 price return (myPurse ! deposit(10, paymentP)).then(_ => good); } The old deposit method returned undefined or threw. The new deposit method itself returns a promise-for-undefined that either fulfills to undefined or rejects. However, in both cases, the promise for what the deposit method will return remains the same, and so the buy method above did not become burdened with having to do a doubly nested then in order to find out whether the deposit succeeded. In addition, our first example line of client code var ackP = paymentP ! deposit(10, myPurse); did not have to change at all. The Q(srcP).then at the beginning of the deposit method will turn a purse into a promise for a purse, but will not turn a promise for a purse into a promise for a promise for a purse. The ackP remains a one-level promise whose fulfillment or rejection indicates whether the deposit succeeds or fails. Call this refactoring "shifting the burden of postponement". I hope this gives some sense about why those who've experienced such patterns like them. And I hope this provides a concrete and meaningful challenge to those who think promises should work otherwise. On Thu, Apr 25, 2013 at 2:22 AM, David Bruant <[email protected]> wrote: > Le 24/04/2013 19:41, Andreas Rossberg a écrit : > >> On 24 April 2013 19:20, Mark S. Miller <[email protected]> wrote: >> >>> On Wed, Apr 24, 2013 at 10:14 AM, Tab Atkins Jr. <[email protected]> >>> wrote: >>> >>>> Q and similar libraries don't actually assume that a Future<Future<x>> >>>> is a Future<x>. >>>> >>> Yes it does. Except of course that we call these "promises". Please see >>> the >>> extensive discussions on the Promises/A+ site about why this flattening >>> behavior is important. >>> >> That strikes me as a very odd design decision, since it would seem to >> violate all sorts of structural and equational invariants. >> > From a developer perspective, my experience using promises is that it's an > extremely convenient property. Basically, if you can only have x and > Future<x>, but never Future<Future<x>>, you never have to worry about the > resolved value. You know it's always a non-Future value. And that's what > you want. When you call getQueryResult(query).then(**function(result){...}), > you always want result to be something you can play with, not a promise. > Unless you're writing the promise infrastructure, you don't want to have to > worry about that. > > If Future<Future<x>> can exist, then you'll have to write this boilerplate > code in a lot of places: > f.then(function res(v){ > if(Future.isFuture(v)){ > v.then(res); > } > else{ > // actual work with the resolved value. > } > }) > > The promise library taking care of this boilerplate for you is a good > property in my opinion. It also helps making chaining more usable and ease > refactoring: > > var p2 = p.then(function(x){ > return somethingWith(x); > }) > > Whether somethingWith(x) returns a promise or non-promise value, you know > that in p2.then(), you'll be dealing with a non-promise value. If, for > whatever reason, you change somethingWith(x); to return a promise instead > of a non-promise (or vice-versa), none of your code (beyond the > somethingWith body) needs to be changed (assuming, you're only returning > the value as it's the case here). > I've had only once to change the signature of a function from a > non-promise to a promise and that property was really useful. > > As soon as you have a decent code base with a lot of functions returning > and playing with promises, it's nice to not have to worry about "a promise > for a promise" and have the promise infrastructure doing the flattening > work for you. > > David > > ______________________________**_________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/**listinfo/es-discuss<https://mail.mozilla.org/listinfo/es-discuss> > -- Text by me above is hereby placed in the public domain Cheers, --MarkM
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

