- From: Tab Atkins Jr. <jackalmage@gmail.com>
- Date: Sat, 27 Apr 2013 20:31:45 -0700
- To: Mark Miller <erights@gmail.com>
- Cc: 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>, Ron Buckton <rbuckton@chronicles.org>, David Bruant <bruant.d@gmail.com>, Dean Tribble <tribble@e-dean.com>
On Sat, Apr 27, 2013 at 5:46 PM, Mark Miller <erights@gmail.com> wrote: > I am worried that we're again separated by a common terminology more than we > are by an actual technical disagreement. I am arguing against an > unconditional lift operation that would make a promise-for-promise. Or at > least seeking to provoke someone to provide a compelling example showing why > this is useful[1]. What all the recent messages seem to be arguing for is > the existence of a non-assimilating and perhaps a non-unwrapping lift-ish > operation, so that one can make a promise-for-thenable. I have no objection > to being able to make a promise-for-thenable. The reasoning *for* an unconditional lift operator is that promises, as specified by DOM Futures or Promises/A+, are roughly a monad, and having an unconditional lift is required to satisfy the monad laws. Being a monad is useful, in ways similar to how being an iterable is useful, or an array-like, or many other typeclasses that various languages recognize. For example, Future.all() is almost equivalent to a variadic liftA() (name taken from Haskell), which takes a function and any number of arguments, all wrapped in some particular monad, and executes the function with its arguments in the way that the monad prefers. For promises, that means "if every promise accepts, execute the function and return an accepted promise for the return value; otherwise, return a rejected promise". This sort of function comes *for free* once a class has been established as a monad, just as Python's very useful itertools library applies "for free" to anything that establishes itself as an iterable. You don't have to write any special code to make it happen; just the fact that promises are monads means that the generically-written liftA() method automatically works. > The clearest sign to me of the potential for misunderstanding is the use of > the term "flattening" for unwrapping of thenables. To be clear, "flattening" > is about promises -- it is just the conditional autolifting seen from the > other side (as I now understand the term autolifting -- thanks). > "unwrapping" is what happens to thenables. "Assimilation" is recursive > unwrapping of thenables. I understand that it can be difficult to keep these > distinctions straight. I wouldn't be surprised if I've been sloppy myself > with these terms earlier in these threads. But now that we're zeroing in, > these fine distinctions matter. I'm not sure where you got this precise terminology, but I'm willing to adopt it for these discussions if it helps reduce confusion. > As I demonstrated somewhere in one of the Promises/A+ threads, I don't think > it is practical to prohibit these promises-for-thenables anyway. As an > example, let's take Q's use of Q as an allegedly fully assimilating lift-ish > function. Like an autolifter Q(promise) returns that promise. And Q(x) where > x is neither a promise nor a thenable, returns promise-for-x. The > controversial part -- which I fully sympathize with since it is a dirty hack > -- is that Q(thenable) assimilates -- it does recursive unwrapping (NOT > "flattening") of the thenable. Assimilation aside, Q is an autolifter, so > I'll can it an "assimilating autolifter". Assume a system is which this Q > function and .then are the only lift-ish operations, and that .then is also > an assimilating autolifter. What guarantee is supposed to follow? .then() *cannot* be assimilating, nor can the default autolifter. Assimilation needs to be something explicit and separate, or else things will mysteriously break when you mix in objects like from Casper.js. It's not cool, nor is it necessary, for promises to forever poison the landscape of "objects that happen to have a method named 'then'". > Assuming that the thenable check is if(typeof x.then === 'function'), > intuitively, we might think that > > Q(p).then(shouldntBeThenable => alert(typeof shouldntBeThenable.then)); > > should never alert 'function'. But this guarantee does not follow: > > var p = {}; > Q(p).then(shouldntBeThenable => alert(typeof shouldntBeThenable.then)); > p.then = function() { return "gotcha"; }; > > This is a consequence of assimilation being a dirty hack. The notion of a > thenable is only marginally more principled than the notion of array-like. > It is not a stable property. Above, p became a thenable after Q already > judged it to be a non-promise non-thenable and hence returned a > promise-for-p. This promise scheduled a call to the callback before p became > a thenable, but p became a thenable before the callback got called. Alleged > guarantee broken. > > Thus, no fundamental guarantee would be lost by introducing an expert-only > operation that does autolifting but no unwrapping. This would make > convenient what is possible anyway: promises-for-thenables. But this is > *not* an argument for introducing a full lifting operation. Introducing that > would break an important guarantee -- that there are no > promises-for-promises. Without full lifting, promises-for-promises remain > impossible. Why do think that "no promises-for-promises" is an important guarantee? > I leave it to monad fans and/or haters of assimilation to suggest names for > this convenient operation, a non-unwrapping autolifter. I'm confident that > if I tried to name it, I'd only cause more confusion ;). It should just be called "resolve", as in Future.resolve(). > [1] FWIW, if there's interest, I can provide several examples where a > promise-for-promise is useful, but I know of no compelling example. The > utility of the examples I have in mind are not worth the cost of introducing > this possibility of a promise-for-promise. Given the demonstrated difficulty of accidentally creating a promise-for-a-promise, why do you feel it is so important to guarantee that it can't ever happen? ~TJ
Received on Sunday, 28 April 2013 03:32:32 UTC