- From: Ron Buckton <rbuckton@chronicles.org>
- Date: Mon, 29 Apr 2013 20:07:05 +0000
- To: Tab Atkins Jr. <jackalmage@gmail.com>
- CC: Mark Miller <erights@gmail.com>, David Sheets <kosmo.zb@gmail.com>, "Mark S. Miller" <erights@google.com>, es-discuss <es-discuss@mozilla.org>, "public-script-coord@w3.org" <public-script-coord@w3.org>, David Bruant <bruant.d@gmail.com>, Dean Tribble <tribble@e-dean.com>
> -----Original Message----- > From: Tab Atkins Jr. [mailto:jackalmage@gmail.com] > Sent: Monday, April 29, 2013 11:21 AM > To: Ron Buckton > Cc: Mark Miller; David Sheets; Mark S. Miller; es-discuss; public-script- > coord@w3.org; David Bruant; Dean Tribble > Subject: Re: A Challenge Problem for Promise Designers > > On Mon, Apr 29, 2013 at 11:03 AM, Ron Buckton <rbuckton@chronicles.org> > wrote: > > Thanks for the clarifications re: Future as a monad. My understanding of > this is as follows (please correct me if I am wrong): > > > > * The expected result of the resolve/reject callbacks passed to > Future#then is itself a Future. > > * If the result of the resolve/reject callbacks is not a Future it is logically > lifted into a Future, by accepting the value on the Future that is the result of > Future#then. > > * The Future result of the callback is merged into the Future returned > Future#then then by chaining to either Future#then (Future#done?) of the > callback result. > > * Given this, it is not usually necessary to "recursively unwrap" or "flatten" > a Future. As a result Future#then should not flatten the result. This would > preserve Future.accept(Future.accept(value)).then().then(F => /* F is > Future(value) */). > > All correct. (Some of your precise mechanical details are probably wrong, but > you got all the important bits.) I updated [1] my rough implementation of Future based on this discussion. This has the following changes from the previous [2] version which was based on the DOM spec for Future: * The resolver's resolve algorithm tests value to determine if it is a Future instance (rather than a "thenable"). This could later be done by checking branding or by checking for a symbol. * The resolver's resolve algorithm only unwraps the value once if it is a Future, rather than performing flattening. It does this by calling the resolver's accept algorithm in the "resolve" future callback for rather than the resolve algorithm. * In the steps for Future#then, if the "resolveCallback" is null, the "resolve" callback becomes a future callback for resolver and its accept algorithm. This is to preserve the value for something like: Future.accept(Future.accept(value)).then().then(F => /* F is Future(value) */) Future.accept(Future.accept(Future.accept(value))).then().then(FF => /* FF is Future(Future(value)) */) * In the steps for some/any/every, the future callbacks that are created that used the resolver's resolve algorithm now use the resolver's accept algorithm. This is to preserve the value for something like: Future.any(Future.accept(Future.accept(value))).then(F => /* F is Future(value) */) Future.any(Future.accept(Future.accept(Future.accept(value)))).then(FF => /* FF is Future(Future(value)) */) * Added Future.from to perform explicit assimilation (with only one level of unwrap, as with Future#then) * Added Future.isFuture to test for native Futures Hopefully I've captured the mechanical details correctly in the implementation. [1, updated implementation] https://github.com/rbuckton/promisejs/blob/master/Future1/Future.ts [2, spec implementation] https://github.com/rbuckton/promisejs/blob/master/Future0/Future.ts Ron > > > I can also agree that it is inherently unsafe to assimilate "thenables" for > Future.resolve/FutureResolver#resolve, due to possible collisions with the > property name on existing objects such as with casper.js. This kind of collision > is the same rationale for having an @iterator symbol for iterators, though I > wouldn't think the same resolution is likely the best result in this case. I am of > the opinion that native Futures should only resolve native Futures (or > possibly their subclasses). To assimilate you could have a Future.of (or > possibly Future.from to match Array.from for "array-like" objects), which > would assimilate "future-like" objects (i.e. "thenables"), but only via one > level of unwrapping and not recursively. > > I'm fine with the concept of using branding (via a symbol) to denote that > something should be treated like a future. I'm also fine with only allowing > Promises and subclasses. Whatever people decide is better. > > Domenic, after a lot of experience, thinks that the assimilation procedure > should be recursive (presumably "bottoming out" when it hits a native > promise). I'm fine with that. It does mean that if you need to assimilate a > thenable for a Casper object, it'll do the wrong thing, but that's the price you > pay for arbitrary library compat. If you know your foreign thenable is only a > single layer, you can safeguard its contents yourself, by chaining .then() and > returning a native Promise holding the value. Then, the assimilation > procedure will "eat" the thenable, hit the Promise and adopt its state, and > the Casper object (or whatever) will be safe. > > > I'm still concerned about cancellation, but will spin up another thread to > hopefully spark some thoughtful discussion on the topic. > > Go for it. I have given some thought to it, and think I have a reasonable > model. > > ~TJ
Received on Monday, 29 April 2013 20:07:39 UTC