The Paradox of Partial Parametricity

Tab Atkins Jr. jackalmage at gmail.com
Wed May 22 21:19:09 PDT 2013


On Wed, May 22, 2013 at 6:04 PM, Domenic Denicola
<domenic at domenicdenicola.com> wrote:
> I found it [a fun exercise](https://gist.github.com/domenic/5632079) to show how little code it takes to build unabashed monadic promises on top of Q-like promises. (It's been a while since I exercised those brain-muscles, so any corrections appreciated.) The punch line is
>
> ```js
> function unit(x) {
>     return Q({ x });
> }
>
> function bind(m, f) {
>     return m.then({ x } => f(x));
> }
> ```
>
> My interpretation of this exercise---apart from the fact that I miss doing mathematical proofs---is that, since it's so little code to implement unabashed monadic promises on top of Q-like promises, and Q-like promises have proven their worth in JavaScript whereas unabashed monadic promises have not, it makes much more sense to standardize on Q-like promises as the base, and leave unabashed monadic promises to user-space libraries.
>
> (Abashed monadic promises are, of course, a failure mode of standardization---as Mark points out---and not really worth considering.)

It's a weird wrapper object whose sole reason for existing is to
defeat the auto-unwrapping.  That's so ugly. ;_;

Try this proposal out instead:

Promises stack.  Nothing magical, they're just containers that can
contain anything, including more promises.

.then() waits until it can resolve to a plain value (delaying until
nested promises resolve) before calling its callbacks.  The callback
return values have the current spec magic, where you can return either
a plain value or a promise, and in the latter case the chained promise
unwraps it once and adopts its state.  You can return nested promises
here, but you'll never see them as long as you use .then().

.chain() has the same signature, but doesn't wait - it calls its
callbacks as soon as it resolves, with whatever value is inside of it,
whether it's a plain value or another promise.  Callback return values
have to be promises, or else it throws.  (This means that it's the
monadic operation, with no caveats.)

The remaining two methods, .done() and .catch(), match .then() for
convenience/conceptual integrity.

This proposal keeps the nice, conceptual simplicity of the promises
model, and gives you a choice of how to handle it, whether you want
the immediate underlying value or just the plain value after they all
resolve.  You can mix and match mid-stream if you'd like; you're not
locked into one or the other as soon as you start using one of them
(unlike the use-a-wrapper proposal).

This is, I think, a minimal surface-area, maximum ability proposal,
which addresses both the single-unwrapping use-cases and the
recursive-unwrapping convenience factor at the same time.  The methods
that people may be familiar with from their existing promise usage is
the "convenient" one with the nice magic, while "chain" matches the
nascent monadic efforts in JS and has a nice distinct name (which I
think communicates better about the behavior, too, where you have to
return a promise).

Thoughts?

~TJ


More information about the es-discuss mailing list