A Challenge Problem for Promise Designers

David Sheets kosmo.zb at gmail.com
Sat Apr 27 09:55:33 PDT 2013


On Sat, Apr 27, 2013 at 5:21 PM, Ron Buckton <rbuckton at chronicles.org> wrote:
> Here is a case where flattening helps:
>
> function executeAndWaitForComplete(command) {
>   return getJSON(commandUrl + command)
>     .then(function (commandResult) {
>       if (commandResult.complete) {
>         return commandResult.model;

This gets automatically lifted into Future, resulting in Future<typeof
commandResult.model>.

>       }
>       var statusUrl = commmandResult.statusUrl;
>       return pollForCommandComplete(statusUrl);

This is always a Future and becomes the result of
executeAndWaitForComplete with type Future<typeof
commandResult.model>.

>     })
> }
>
> function pollForCommandComplete(statusUrl) {
>   return new Future(function(resolver) {
>     var poll = function (pollResult) {
>       if (pollResult.done) {
>         resolve.resolve(pollResult.model);
>       }
>       else {
>         setTimeout(function() {
>           getJSON(statusUrl).done(poll, resolver.reject);
>         }, 500);
>       }
>     }
>     getJSON(statusUrl).done(poll, resolver.reject);
>   });
> }
>
> In this example, the server will receive a command from the client and will
> process it asynchronously. The client then needs to poll an endpoint to
> check for completion of the command. Just using Futures, flattening is
> helpful here. Without flattening, executeAndWaitForComplete would could
> return either a Future<object> OR return a Future<Future<object>>.

I think the major point of confusion in these discussions is the
result of the framing of the discussion in terms of "flattening". I
believe most beneficial viewpoint is that of "autolifting".

That is, the exceptional case is not when the function argument of
"then" returns a Future+ that gets "flattened" but rather when the
function argument of "then" returns a non-Future that gets
automatically lifted into a Future.

This change in perspective is non-obvious because in many of these
APIs there is no succinct lifting operation to make a Future from
another value. This is a major reason why something like Future.of
(Future.accept) is important.

> In a non
> flattening world, the developer would have to change the function to:
>
> function executeAndWaitForComplete(command) {
>   return new Future(function(resolver) {
>     getJSON(commandUrl + command)
>       .done(function (commandResult) {
>         if (commandResult.complete) {
>           resolver.resolve(commandResult.model);
>         }
>         var statusUrl = commmandResult.statusUrl;
>         pollForCommandComplete(statusUrl).done(resolver.resolve,
> resolver.reject);
>       }, resolver.reject)
>     });
> }

In a non-flattening (and non-autolifting) world, this would be the code:

function executeAndWaitForComplete(command) {
  return getJSON(commandUrl + command)
    .then(function (commandResult) {
      if (commandResult.complete) {
        return Future.accept(commandResult.model);
      }
      var statusUrl = commmandResult.statusUrl;
      return pollForCommandComplete(statusUrl);
    })
}

The only difference here is that commandResult.model is explicitly
wrapped. This is because the signature of then is actually Future<a>
-> (a -> Future<b>) -> Future<b>.

David


More information about the es-discuss mailing list