- From: David Sheets <kosmo.zb@gmail.com>
- Date: Sat, 27 Apr 2013 19:07:07 +0100
- To: "Mark S. Miller" <erights@google.com>
- Cc: es-discuss <es-discuss@mozilla.org>, "public-script-coord@w3.org" <public-script-coord@w3.org>, Ron Buckton <rbuckton@chronicles.org>, David Bruant <bruant.d@gmail.com>, Mark Miller <erights@gmail.com>, "Tab Atkins Jr." <jackalmage@gmail.com>, Dean Tribble <tribble@e-dean.com>
On Sat, Apr 27, 2013 at 6:05 PM, Mark S. Miller <erights@google.com> wrote: > On Sat, Apr 27, 2013 at 9:55 AM, David Sheets <kosmo.zb@gmail.com> wrote: > [...] >> >> 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. > > I was following you until this last paragraph. As you define autolifting in > the first two paragraphs, Q(x) would be an autolifting operation. It has the > signature: > > promise<t> -> promise<t> > or > t -> promise<t> // if t is not itself a promise type > > Are you distinguishing "autolifting" vs "lifting"? If so, why do you think > it is important or desirable to provide a lifting operation (as opposed to > an autolifting operation)? Yes. Autolifting is conditional on promise-ness. Lifting is fully parametric. If the standard uses autolifting instead of recursive flattening, many of the headaches with "thenables" go away and we gain enormous flexibility in future interoperation with the spec. For instance, if your code might manipulate objects which have callable "then" fields but which don't subscribe to the promises spec, it is safest to always use: return Promise.of(myMaybeNonPromiseThenable); This greatly reduces the criticality of the "is this a promise?" predicate because in most cases you will simply return a non-thenable (autolifted) or a promise-like thenable and not care. In those cases where you wish to put non-promise thenable inside of a promise or *don't know if someone else will want to*, the explicit use of the lifting operation lets you avoid autolifting/flattening. This massively simplifies the protocol between the promises spec and those values it encapsulates by only ever making a single assumption that then-returned thenables are promise-like but their contents are *totally opaque*. I believe this design results in the maximal flexibility and safety for the platform by supplying a handy autolifting "then" while also allowing people to easily subscribe to promise interaction (by then-returning a thenable), defend their thenables from magical unwrapping (by explicitly using Promise.of), and write completely polymorphic code. With this design, in the most common case, developers won't have to use Promise.of. Perhaps the only common use will be in starting a promise chain from a constant: var promise; if (just_use_a_constant) { promise = Promise.of(6); } else { promise = getAsyncValue(); } promise.then(function (x) { return x*2); }); While those who translate code from other languages, target their compilers to JS Promises, write polymorphic libraries, use non-promises with callable "then" fields, or invent new interoperable promise-like (but distinct) semantics won't have to worry about hacking around recursive unwrapping. To me, having some standard for promise-like objects in the platform seems very fundamental for handling asynchrony, ordering, failure, need, and probability. If we consider promise-like objects as fundamental, we should investigate the properties of their operations: With recursive flattening "then" operation, the time complexity of "then" is O(n) with n the number of nested promise-like objects. With autolifted "then" operation, the time complexity of "then" is O(1). Here, I am using time complexity as a proxy for the mental complexity of the operation and not as a proxy for execution performance (recursive unwrapping is usually of static depth as we have seen). You can see that not only does the recursive flattening involve a hidden loop that the advanced programmer must reason about but also invokes the notion of "promise-like object" which, as we have seen, leads to all sorts of tangents regarding how to characterize the property of promise-ness and still maintain clarity, safety, extensibility, and ease-of-use. I hope this explanation satisfies you. If not, I am more than happy to answer any questions you may have about this approach. Warm regards, David Sheets
Received on Saturday, 27 April 2013 18:13:22 UTC