Unify promises

251 views
Skip to first unread message

Irakli Gozalishvili

unread,
Nov 23, 2010, 8:48:03 AM11/23/10
to CommonJS, Kris Kowal, Kris Zyp
Hi,

I know last few attempts to unify promise API did not got us anywhere, but I still would like to give it another try, because I believe that having no consensus is partially a reason for a low adoption of promises in the js community. My
suggestion would be reducing promises API to bare minimum, which would allow both approaches (then-able promises / q) to be build on top. Interoperability on a base level will allow us to move forward without reinventing same wheel with a different flavors and everyone will benefit from that.

I'm still fairly new to promises and please bare with me if I miss something important. I wrote [this proposal](https://gist.github.com/711766) which I believe allows us to implement both of the proposed APIs as layer
on top or as set of utilities without compromising important points on both camps.


I can also provide implementation snippets if necessary on how then or when can be implemented on top.

Regards
--
Irakli Gozalishvili
Web: http://www.jeditoolkit.com/
Address: 29 Rue Saint-Georges, 75009 Paris, France

Irakli Gozalishvili

unread,
Nov 23, 2010, 9:40:40 AM11/23/10
to CommonJS, Kris Kowal, Kris Zyp

Kris Zyp

unread,
Nov 23, 2010, 10:49:32 AM11/23/10
to Irakli Gozalishvili, CommonJS

On 11/23/2010 6:48 AM, Irakli Gozalishvili wrote:
> Hi,
>
> I know last few attempts to unify promise API did not got us anywhere,
> but I still would like to give it another try, because I believe that
> having no consensus is partially a reason for a low adoption of
> promises in the js community. My
> suggestion would be reducing promises API to bare minimum, which would
> allow both approaches (then-able promises / q) to be build on top.
> Interoperability on a base level will allow us to move forward without
> reinventing same wheel with a different flavors and everyone will
> benefit from that.

http://wiki.commonjs.org/wiki/Promises/A has a single required method. I
don't follow what you are reducing if you create a new proposal that 3
or 4 methods/constructors. Promises/A is the bare minimum (unless you
think we can specify promises with less than 1 construct ;) ), and
Promises/B can already be implemented on it except for the opaque
promise clause.

>
> I'm still fairly new to promises and please bare with me if I miss
> something important. I wrote [this
> proposal](https://gist.github.com/711766) which I believe allows us to
> implement both of the proposed APIs as layer
> on top or as set of utilities without compromising important points on
> both camps.

The only incompatibility between Promises/A and /B is that /B explicitly
forbids compatibility with /A (the opaque promise clause). I don't know
you achieve interoperability with a specification that forbids
interoperability! Modulo this clause, I believe /A and /B are already
easily unifiable (/B can readily be built on /A), in fact I think the
promise module from promised-io more or less implements /A and /B right now.

--
Thanks,
Kris

Kris Kowal

unread,
Nov 23, 2010, 12:35:11 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 7:49 AM, Kris Zyp <kri...@gmail.com> wrote:
> http://wiki.commonjs.org/wiki/Promises/A has a single required method. I
> don't follow what you are reducing if you create a new proposal that 3
> or 4 methods/constructors. Promises/A is the bare minimum (unless you
> think we can specify promises with less than 1 construct ;) ), and
> Promises/B can already be implemented on it except for the opaque
> promise clause.

I agree with Kris* that this proposal does less to unify and more to
separate two already nearly compatible proposals.

I'm also still adamant that Promises/A is simpler but too simple and
that Promise/B must be the base. It's stricter (which is good in my
opinion) and quite simple.

It seems more likely to me that this issue can only be resolved in two ways:

1.) Kris* and I switch places for a month and use and develop on each
other's respective systems and come back with nice things to say about
each other's ideas.

2.) Elect an arbiter from the small collection of people who have used
both systems and agree, for the purpose of CommonJS, to abide their
judgement as to which proposal gets eliminated and which gets pursued.
As far as I know, Irakli and Nathan Stott are the only two candidates.

The trouble with 2 is that I, at this point, would not be willing to
discontinue my work on Promises/B stuff regardless of what gets
adopted by CommonJS (which is unfortunate but not critical for
progress), and the trouble with 1 is that I don't have a whole lot of
time. (For context, I have given myself a couple months to work on my
pet projects before I consider getting a real job. One of these
projects is built on Promises/B.)

So maybe, Kris* and I need to have a peace summit or something.

One concession I can definitely give: If people use the Promises/B API
(which has been implemented for Promises/A successfully), Nathan has
verified that such code will work when using a Promises/A
implementation. However, code written for Promises/A does not
necessarily work on a Promises/B implementation. It is possible within
Promises/B for the opaque promise object to actually be a Promises/A
thenable (if you don't use Q.get, Q.put, Q.post, &c) IF users
construct thenables exclusively with the Q API. There are
disadvantages to this approach (thenable promises can be confused with
thenable values, without being able to exercise the advantages of
thenables), but you could get by in most cases.

It remains clear to me that Promises/B is the firmer ground for a
specification and Promises/A is better left as an option for
implementors.

Kris**

Irakli Gozalishvili

unread,
Nov 23, 2010, 12:35:42 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 16:49, Kris Zyp <kri...@gmail.com> wrote:


On 11/23/2010 6:48 AM, Irakli Gozalishvili wrote:
> Hi,
>
> I know last few attempts to unify promise API did not got us anywhere,
> but I still would like to give it another try, because I believe that
> having no consensus is partially a reason for a low adoption of
> promises in the js community. My
> suggestion would be reducing promises API to bare minimum, which would
> allow both approaches (then-able promises / q) to be build on top.
> Interoperability on a base level will allow us to move forward without
> reinventing same wheel with a different flavors and everyone will
> benefit from that.

http://wiki.commonjs.org/wiki/Promises/A has a single required method. I
don't follow what you are reducing if you create a new proposal that 3
or 4 methods/constructors. Promises/A is the bare minimum (unless you
think we can specify promises with less than 1 construct ;) ),

From my view Promises/A just omits many things that needs to be specified but that is less important then note below:
 
and
Promises/B can already be implemented on it except for the opaque
promise clause.


Yes that exactly why we do have two camps. I also do agree with KrisK that **opaque** is must have.
 
>
> I'm still fairly new to promises and please bare with me if I miss
> something important. I wrote [this
> proposal](https://gist.github.com/711766) which I believe allows us to
> implement both of the proposed APIs as layer
> on top or as set of utilities without compromising important points on
> both camps.

The only incompatibility between Promises/A and /B is that /B explicitly
forbids compatibility with /A (the opaque promise clause). I don't know
you achieve interoperability with a specification that forbids
interoperability!

I thin you are misunderstanding the point that was made on the opaque promise clause. Yes it's true that building opaque promise on top of non-opaque is impossible, while it's pretty much possible other way round. That's more or less what I'm suggesting.  

I don't know if you sew my naive implementation examples, but what I'm suggesting to have much more minimalistic opaque promise that can easily be extended to a thenable. Also if Q API will be build on top promises produced by both libraries would be able to talk with each other.

then-able promises can be created like this:

Object.create(Promise.make().promise, ThenableDescriptor)

Q promises can be converted to thenable like this:

Object.create(Q.defer().promise, ThenableDescriptor)

Finally thenable promises can be used by Q api like this:

Q.when(Object.getPrototypeOf(thenablePromise), ....



Modulo this clause, I believe /A and /B are already
easily unifiable (/B can readily be built on /A), in fact I think the
promise module from promised-io more or less implements /A and /B right now.


opaque clause  is only reason why we have two camps today and ignoring this clause really won't get us anywhere. Is there any reason why you don't want to build thenable promises on top of opaque ones ?
 
--
Thanks,
Kris

--
You received this message because you are subscribed to the Google Groups "CommonJS" group.
To post to this group, send email to comm...@googlegroups.com.
To unsubscribe from this group, send email to commonjs+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/commonjs?hl=en.


Kris Zyp

unread,
Nov 23, 2010, 12:44:05 PM11/23/10
to comm...@googlegroups.com, Irakli Gozalishvili
Opaque and non-opaque are opposite, you can't build one on top of the other, regardless of the order.


I don't know if you sew my naive implementation examples, but what I'm suggesting to have much more minimalistic opaque promise that can easily be extended to a thenable. Also if Q API will be build on top promises produced by both libraries would be able to talk with each other.

then-able promises can be created like this:

Object.create(Promise.make().promise, ThenableDescriptor)

Q promises can be converted to thenable like this:

Object.create(Q.defer().promise, ThenableDescriptor)

Finally thenable promises can be used by Q api like this:

Q.when(Object.getPrototypeOf(thenablePromise), ....

You have thenable, but it isn't the Q promise, you now have two different promise forms, you done nothing to unify them.




Modulo this clause, I believe /A and /B are already
easily unifiable (/B can readily be built on /A), in fact I think the
promise module from promised-io more or less implements /A and /B right now.


opaque clause  is only reason why we have two camps today and ignoring this clause really won't get us anywhere. Is there any reason why you don't want to build thenable promises on top of opaque ones ?

It is self-contradictory.

-- 
Thanks,
Kris

Irakli Gozalishvili

unread,
Nov 23, 2010, 12:45:07 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 18:35, Kris Kowal <kris....@cixar.com> wrote:
On Tue, Nov 23, 2010 at 7:49 AM, Kris Zyp <kri...@gmail.com> wrote:
> http://wiki.commonjs.org/wiki/Promises/A has a single required method. I
> don't follow what you are reducing if you create a new proposal that 3
> or 4 methods/constructors. Promises/A is the bare minimum (unless you
> think we can specify promises with less than 1 construct ;) ), and
> Promises/B can already be implemented on it except for the opaque
> promise clause.

I agree with Kris* that this proposal does less to unify and more to
separate two already nearly compatible proposals.

I'm also still adamant that Promises/A is simpler but too simple and
that Promise/B must be the base. It's stricter (which is good in my
opinion) and quite simple.

It seems more likely to me that this issue can only be resolved in two ways:

1.) Kris* and I switch places for a month and use and develop on each
other's respective systems and come back with nice things to say about
each other's ideas.

2.) Elect an arbiter from the small collection of people who have used
both systems and agree, for the purpose of CommonJS, to abide their
judgement as to which proposal gets eliminated and which gets pursued.
As far as I know, Irakli and Nathan Stott are the only two candidates.


I don't think either one or another is going to work out. People will continue to use whatever they prefer, at least I will. My main attention was to suggest common ground to make promises interchangeable.
 
The trouble with 2 is that I, at this point, would not be willing to
discontinue my work on Promises/B stuff regardless of what gets
adopted by CommonJS (which is unfortunate but not critical for
progress), and the trouble with 1 is that I don't have a whole lot of
time. (For context, I have given myself a couple months to work on my
pet projects before I consider getting a real job. One of these
projects is built on Promises/B.)

So maybe, Kris* and I need to have a peace summit or something.

One concession I can definitely give: If people use the Promises/B API
(which has been implemented for Promises/A successfully), Nathan has
verified that such code will work when using a Promises/A
implementation.  However, code written for Promises/A does not
necessarily work on a Promises/B implementation. It is possible within
Promises/B for the opaque promise object to actually be a Promises/A
thenable (if you don't use Q.get, Q.put, Q.post, &c) IF users
construct thenables exclusively with the Q API. There are
disadvantages to this approach (thenable promises can be confused with
thenable values, without being able to exercise the advantages of
thenables), but you could get by in most cases.

It remains clear to me that Promises/B is the firmer ground for a
specification and Promises/A is better left as an option for
implementors.

Kris**

Dean Landolt

unread,
Nov 23, 2010, 12:45:46 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 12:35 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:

On Tue, Nov 23, 2010 at 16:49, Kris Zyp <kri...@gmail.com> wrote:


On 11/23/2010 6:48 AM, Irakli Gozalishvili wrote:
> Hi,
>
> I know last few attempts to unify promise API did not got us anywhere,
> but I still would like to give it another try, because I believe that
> having no consensus is partially a reason for a low adoption of
> promises in the js community. My
> suggestion would be reducing promises API to bare minimum, which would
> allow both approaches (then-able promises / q) to be build on top.
> Interoperability on a base level will allow us to move forward without
> reinventing same wheel with a different flavors and everyone will
> benefit from that.

http://wiki.commonjs.org/wiki/Promises/A has a single required method. I
don't follow what you are reducing if you create a new proposal that 3
or 4 methods/constructors. Promises/A is the bare minimum (unless you
think we can specify promises with less than 1 construct ;) ),

From my view Promises/A just omits many things that needs to be specified but that is less important then note below:
 
and
Promises/B can already be implemented on it except for the opaque
promise clause.


Yes that exactly why we do have two camps. I also do agree with KrisK that **opaque** is must have.

When issue I have with opaque promises is how they could be made to work in a sandboxed environment? Wouldn't you have to do some magic like injecting the promise lib as part of the environment and catching any require("promise") calls to satisfy the instanceof check? Is there any cleaner way?

I would imagine securable trumps opaque, right?

[snip the rest]

Irakli Gozalishvili

unread,
Nov 23, 2010, 12:52:33 PM11/23/10
to Kris Zyp, comm...@googlegroups.com

Both of them are layers on top of (promise/c) if you like and while indeed B and A are not unified by this, this would our libraries to talk with each others which is not the case at the moment.   

Kris Kowal

unread,
Nov 23, 2010, 1:01:18 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 9:44 AM, Kris Zyp <kri...@gmail.com> wrote:
> Opaque and non-opaque are opposite, you can't build one on top of the other,
> regardless of the order.

That's certainly not the intent of the opacity clause. It is meant to
say that the consumer of the Q API cannot depend any further
implementation details of the returned promises, e.g., thenability.
They are opaque in the sense that the consumer cannot see behind them.
You certainly can build Promises/B on top of Promises/A for the
purpose of Q.when. The specification addresses use, not construction.

Does this change anything for you?

Kris Kowal

Nathan Stott

unread,
Nov 23, 2010, 1:04:55 PM11/23/10
to comm...@googlegroups.com
I will chime in more later when I have more time to compose my thoughts.

Right now this is an important issue to me as Bogart is built on of
Zyp promises but I want to use Jaque in my projects as well which is
built on Kowal promises.

It was my impression, until I actually tried it, that as long as I
avoided using '.then' and only used 'Q.when' that the promise
implementation wouldn't matter. This is not the case. I wish it
were and some way to get us to that point would be most welcome.

More to come later.

Kris Kowal

unread,
Nov 23, 2010, 1:05:39 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 9:45 AM, Dean Landolt <de...@deanlandolt.com> wrote:
> When issue I have with opaque promises is how they could be made to work in
> a sandboxed environment? Wouldn't you have to do some magic like injecting
> the promise lib as part of the environment and catching any
> require("promise") calls to satisfy the instanceof check? Is there any
> cleaner way?

That would indeed be necessary for promises to communicate between the
sandbox and the parent. Promise/B is designed with the intent that
promise objects be frozen in such situations too, so that promises
designated to multiple mutually suspicious sandboxes can safely
communicate unidirectionally.

> I would imagine securable trumps opaque, right?

Unless the purpose of opacity is to satisfy security. It is not safe
to use the thenable API directly because doing so gives the receipient
excess authority: multiple resolution, multiple rejection, same-turn
resolution, and same-turn rejection.

Kris Kowal

Dean Landolt

unread,
Nov 23, 2010, 2:40:29 PM11/23/10
to comm...@googlegroups.com
The `then` is but a registrar, identical to `Q.when` in every way but where it is carried. How could `then` expose resolution or rejection? I was under the impression that it cannot.

Kris Kowal

unread,
Nov 23, 2010, 2:45:59 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 11:40 AM, Dean Landolt <de...@deanlandolt.com> wrote:
> The `then` is but a registrar, identical to `Q.when` in every way but where
> it is carried. How could `then` expose resolution or rejection? I was under
> the impression that it cannot.

In object capabilities, the "promise.then(resolved, rejected)"
function "introduces" the "promise" to your "resolved" and "rejected"
capabilities. That means that the recipient, "promise", can now call
those functions, "exercise those capabilities", at any time from the
point of invocation. That's why the "Q.when" method in Tyler Close's
original code and my derivative "protects" the "resolved" and
"rejected" by wrapping them in a new, trustworthy promise that
guarantees that the resolved and rejected functions can only be called
once, either one or the other, in a future turn of the event loop.

https://github.com/kriskowal/q/blob/master/lib/q.js#L288-298

Kris

Kris Kowal

unread,
Nov 23, 2010, 2:47:14 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 11:45 AM, Kris Kowal <kris....@cixar.com> wrote:
> In object capabilities, the "promise.then(resolved, rejected)"
> function "introduces" the "promise" to your "resolved" and "rejected"
> capabilities. That means that the recipient, "promise", can now call
> those functions, "exercise those capabilities", at any time from the
> point of invocation.  That's why the "Q.when" method in Tyler Close's
> original code and my derivative "protects" the "resolved" and
> "rejected" by wrapping them in a new, trustworthy promise that
> guarantees that the resolved and rejected functions can only be called
> once, either one or the other, in a future turn of the event loop.

These invariants are important not only for security, but for safety
and robust composition. The Q API is much more resilient against
laziness and error than the then API which it conceptually builds on
top of.

Kris Kowal

Dean Landolt

unread,
Nov 23, 2010, 3:05:56 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 1:04 PM, Nathan Stott <nrs...@gmail.com> wrote:
I will chime in more later when I have more time to compose my thoughts.

Right now this is an important issue to me as Bogart is built on of
Zyp promises but I want to use Jaque in my projects as well which is
built on Kowal promises.

It was my impression, until I actually tried it, that as long as I
avoided using '.then' and only used 'Q.when' that the promise
implementation wouldn't matter.  This is not the case.   I wish it
were and some way to get us to that point would be most welcome.



In my experience the issue of interop is most complicated by the difficulties of the top-level namespace. As far as Promises/B goes, when you require("promise") (or require("q") or whatever) you better be sure you're getting the same promise lib as cooperating libraries.

So interop should be feasible -- we just have to be sure we can all get a handle on the same Promise object so we can make the instanceof tests work. It's possible that the Kris Zyp implementation could try to extend require("q").Promise to satisfy the instanceof check.


Dean Landolt

unread,
Nov 23, 2010, 3:17:51 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 2:47 PM, Kris Kowal <kris....@cixar.com> wrote:
On Tue, Nov 23, 2010 at 11:45 AM, Kris Kowal <kris....@cixar.com> wrote:
> In object capabilities, the "promise.then(resolved, rejected)"
> function "introduces" the "promise" to your "resolved" and "rejected"
> capabilities. That means that the recipient, "promise", can now call
> those functions, "exercise those capabilities", at any time from the
> point of invocation.  That's why the "Q.when" method in Tyler Close's
> original code and my derivative "protects" the "resolved" and
> "rejected" by wrapping them in a new, trustworthy promise that
> guarantees that the resolved and rejected functions can only be called
> once, either one or the other, in a future turn of the event loop.

Thanks for the explanation. This helps me understand a little better the opaqueness requirement -- the only rationale that I was aware of before was collision prevention, which doesn't really stand on its own.

Still, how do you propose we solve the namespace problem? Have we made any progress on this? Can we really trust require("promise") will be available on all platforms? Should we standardize on a commonjs prefix a la require("commonjs/promise")? Without a solution to this problem how could Promises/B really be used interoperably?
 

These invariants are important not only for security, but for safety
and robust composition. The Q API is much more resilient against
laziness and error than the then API which it conceptually builds on
top of.

Interesting -- I've found the biggest safety hazard has been synchronous throws -- and neither promise lib can really do much to rectify this. It's just a wart of the API itself that could only be fixed with something like a Q.whenCall (Kris Zyp's suggestion some time ago). Any thoughts on this?

Irakli Gozalishvili

unread,
Nov 23, 2010, 3:34:08 PM11/23/10
to comm...@googlegroups.com
Let me add few more arguments to my proposal:

While I do like Kris Kowal's Q implementation and use it for all my promise needs today, I think that promise spec can and should be more minimal. I made an attempt to do so, obviously it may not look amazing but I believe it can be shaped to an acceptable state. That being said I tried to address few important points:

1. It satisfies opaque clause (I won't go into details on why is it important, Kris did it already)
2. Both Q and then chain-able promises can be built on top which will make them interoperable.
3. To my knowledge streaming is not well expressed by Q, IMO then-ables are better with that, there for my proposal suggests a way to express steams in opaque promise.
4. I've started work on ES Harmony Proxy based promises, then-able promises is no go there for obvious reasons, Q-is better, but 1. It comes with lot's of stuff that is useless with Proxy based promises. 2. 'emit' method on promise objects get's in the way. This is also addressed by suggested proposal.

I really don't want to come up with yet another promise implementation that will be incompatible and will lead to even bigger fragmentation. All needed is just a minimal spec that can address all the points mentioned. I don't care whether it will be promise a/b/c or d as long all of this is addressed.

I really hope you will consider this points and we'll find a ways to move forward. In the and we all wish to have much
broader adoption of promises.

Kris Zyp

unread,
Nov 23, 2010, 3:43:21 PM11/23/10
to comm...@googlegroups.com

If you can't depend on then(), you can't build interoperable promise
promise producers and consumers can you? You're still stuck needing to
have a single authoritative source of promises, right? And what is a
non-opaque opaque promise then?

--
Thanks,
Kris

Dean Landolt

unread,
Nov 23, 2010, 4:01:52 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 3:34 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:
Let me add few more arguments to my proposal:

While I do like Kris Kowal's Q implementation and use it for all my promise needs today, I think that promise spec can and should be more minimal. I made an attempt to do so, obviously it may not look amazing but I believe it can be shaped to an acceptable state. That being said I tried to address few important points:

1. It satisfies opaque clause (I won't go into details on why is it important, Kris did it already)
2. Both Q and then chain-able promises can be built on top which will make them interoperable.
3. To my knowledge streaming is not well expressed by Q, IMO then-ables are better with that, there for my proposal suggests a way to express steams in opaque promise.

While I'm not 100% sure exactly what kind of streams you're referring to here I think this exposes a fundamental miscommunication. The functionality exposed by the signatures of each are equivalent in every way but one: `when` can handle non-promise (typed) values as well as promises. Thus, it is a superset of `then`. AFAIC the chainability of the `then` method is nothing more than a neat side-effect of the need to have a method key to specify on -- you can't rely on it with untrusted inputs and I can't see how it actually adds anything to make it more amenable to streams.

 
4. I've started work on ES Harmony Proxy based promises, then-able promises is no go there for obvious reasons, Q-is better, but 1. It comes with lot's of stuff that is useless with Proxy based promises. 2. 'emit' method on promise objects get's in the way. This is also addressed by suggested proposal.

I really don't want to come up with yet another promise implementation that will be incompatible and will lead to even bigger fragmentation. All needed is just a minimal spec that can address all the points mentioned. I don't care whether it will be promise a/b/c or d as long all of this is addressed.

I'm with you there. But AFAICT it's both better and worse than you think. A and B are almost identical -- interoperable in every way -- but the opaqueness constraint is a fundamental incompatibility. Promises/A solves interop nicely but at the cost of opacity. Promises/B is arguably more correct but requires you to use The One True Promise Library. Oh, and you have to make sure your code gets the same One True Promise Library as the libraries you're exchanging promises with. To date this is an unsolved problem.

Irakli Gozalishvili

unread,
Nov 23, 2010, 4:42:02 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 22:01, Dean Landolt <de...@deanlandolt.com> wrote:


On Tue, Nov 23, 2010 at 3:34 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:
Let me add few more arguments to my proposal:

While I do like Kris Kowal's Q implementation and use it for all my promise needs today, I think that promise spec can and should be more minimal. I made an attempt to do so, obviously it may not look amazing but I believe it can be shaped to an acceptable state. That being said I tried to address few important points:

1. It satisfies opaque clause (I won't go into details on why is it important, Kris did it already)
2. Both Q and then chain-able promises can be built on top which will make them interoperable.
3. To my knowledge streaming is not well expressed by Q, IMO then-ables are better with that, there for my proposal suggests a way to express steams in opaque promise.

While I'm not 100% sure exactly what kind of streams you're referring to here I think this exposes a fundamental miscommunication. The functionality exposed by the signatures of each are equivalent in every way but one: `when` can handle non-promise (typed) values as well as promises. Thus, it is a superset of `then`. AFAIC the chainability of the `then` method is nothing more than a neat side-effect of the need to have a method key to specify on -- you can't rely on it with untrusted inputs and I can't see how it actually adds anything to make it more amenable to streams.

In Q if I'm not miss-lead promise transitions from one state to another and nothing like progress notifications exist. KrisZ had forEach-able promises and then also accepts 3rd onProgress listener that may be called for each delivered chunk. 
 

 
4. I've started work on ES Harmony Proxy based promises, then-able promises is no go there for obvious reasons, Q-is better, but 1. It comes with lot's of stuff that is useless with Proxy based promises. 2. 'emit' method on promise objects get's in the way. This is also addressed by suggested proposal.

I really don't want to come up with yet another promise implementation that will be incompatible and will lead to even bigger fragmentation. All needed is just a minimal spec that can address all the points mentioned. I don't care whether it will be promise a/b/c or d as long all of this is addressed.

I'm with you there. But AFAICT it's both better and worse than you think. A and B are almost identical -- interoperable in every way -- but the opaqueness constraint is a fundamental incompatibility. Promises/A solves interop nicely but at the cost of opacity. Promises/B is arguably more correct but requires you to use The One True Promise Library. Oh, and you have to make sure your code gets the same One True Promise Library as the libraries you're exchanging promises with. To date this is an unsolved problem.

 
Well there is fundamental difference  in way promises are chained, mainly because of opacity. Unless Promise/A will respect that clause no interoperability will be possible.

Irakli Gozalishvili

unread,
Nov 23, 2010, 4:55:37 PM11/23/10
to comm...@googlegroups.com

Yes you can, that exactly what proposal was about. It just defines authority that can issue promises and defines the way promise state can be identified. All types of chaining can be build on top. Promises have to be issued by one module and all the libs: Q,  then-able and whoever else will, be able to define issuers that just issues core promises with their own API's layer on top. All this libraries will be able to ignore layers added by other libs. Sorry I'm not native speaker and I might not be explaining it well enough, but I believe examples here illustrate that pretty well:

https://gist.github.com/711766#gistcomment-14660
 

Dean Landolt

unread,
Nov 23, 2010, 6:15:49 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 4:42 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:

On Tue, Nov 23, 2010 at 22:01, Dean Landolt <de...@deanlandolt.com> wrote:


On Tue, Nov 23, 2010 at 3:34 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:
Let me add few more arguments to my proposal:

While I do like Kris Kowal's Q implementation and use it for all my promise needs today, I think that promise spec can and should be more minimal. I made an attempt to do so, obviously it may not look amazing but I believe it can be shaped to an acceptable state. That being said I tried to address few important points:

1. It satisfies opaque clause (I won't go into details on why is it important, Kris did it already)
2. Both Q and then chain-able promises can be built on top which will make them interoperable.
3. To my knowledge streaming is not well expressed by Q, IMO then-ables are better with that, there for my proposal suggests a way to express steams in opaque promise.

While I'm not 100% sure exactly what kind of streams you're referring to here I think this exposes a fundamental miscommunication. The functionality exposed by the signatures of each are equivalent in every way but one: `when` can handle non-promise (typed) values as well as promises. Thus, it is a superset of `then`. AFAIC the chainability of the `then` method is nothing more than a neat side-effect of the need to have a method key to specify on -- you can't rely on it with untrusted inputs and I can't see how it actually adds anything to make it more amenable to streams.

In Q if I'm not miss-lead promise transitions from one state to another and nothing like progress notifications exist. KrisZ had forEach-able promises and then also accepts 3rd onProgress listener that may be called for each delivered chunk. 
 

 
4. I've started work on ES Harmony Proxy based promises, then-able promises is no go there for obvious reasons, Q-is better, but 1. It comes with lot's of stuff that is useless with Proxy based promises. 2. 'emit' method on promise objects get's in the way. This is also addressed by suggested proposal.

I really don't want to come up with yet another promise implementation that will be incompatible and will lead to even bigger fragmentation. All needed is just a minimal spec that can address all the points mentioned. I don't care whether it will be promise a/b/c or d as long all of this is addressed.

I'm with you there. But AFAICT it's both better and worse than you think. A and B are almost identical -- interoperable in every way -- but the opaqueness constraint is a fundamental incompatibility. Promises/A solves interop nicely but at the cost of opacity. Promises/B is arguably more correct but requires you to use The One True Promise Library. Oh, and you have to make sure your code gets the same One True Promise Library as the libraries you're exchanging promises with. To date this is an unsolved problem.

 
Well there is fundamental difference  in way promises are chained, mainly because of opacity. Unless Promise/A will respect that clause no interoperability will be possible.


Yes, I know -- I was trying to point out that the APIs themselves are completely compatible but was making the point about opacity. That said, a Promise/A implementation could extend the One True Promise constructor to be interoperable with a Promise/B implementation. But this does absolutely nothing for my uneasiness about this assumption that require("promise") will always give you the right library. There's nothing in SecurableModules that can give you this guarantee. This isn't just an academic concern: the possibility of this or any promise implementation ending up in node approaches 0. So you've got to path shift. Unless we come up with something better dependency hell for opaque promises is virtually assured.

Irakli Gozalishvili

unread,
Nov 23, 2010, 7:02:55 PM11/23/10
to comm...@googlegroups.com
 
Ok I see! I think that most of the nodejs community is on npm that sort of guarantees that require('promise') will return the same thing or throw regardless of combination of packages installed. Also here can be something like
require('commonjs/promise') that will provide base promise API that will be used behind the scenes by both require('q') and require('promise') to construct promises.

Dean Landolt

unread,
Nov 23, 2010, 7:34:58 PM11/23/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 7:02 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:


On Wed, Nov 24, 2010 at 00:15, Dean Landolt <de...@deanlandolt.com> wrote:


On Tue, Nov 23, 2010 at 4:42 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:

On Tue, Nov 23, 2010 at 22:01, Dean Landolt <de...@deanlandolt.com> wrote:


On Tue, Nov 23, 2010 at 3:34 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:
Let me add few more arguments to my proposal:

While I do like Kris Kowal's Q implementation and use it for all my promise needs today, I think that promise spec can and should be more minimal. I made an attempt to do so, obviously it may not look amazing but I believe it can be shaped to an acceptable state. That being said I tried to address few important points:

1. It satisfies opaque clause (I won't go into details on why is it important, Kris did it already)
2. Both Q and then chain-able promises can be built on top which will make them interoperable.
3. To my knowledge streaming is not well expressed by Q, IMO then-ables are better with that, there for my proposal suggests a way to express steams in opaque promise.

While I'm not 100% sure exactly what kind of streams you're referring to here I think this exposes a fundamental miscommunication. The functionality exposed by the signatures of each are equivalent in every way but one: `when` can handle non-promise (typed) values as well as promises. Thus, it is a superset of `then`. AFAIC the chainability of the `then` method is nothing more than a neat side-effect of the need to have a method key to specify on -- you can't rely on it with untrusted inputs and I can't see how it actually adds anything to make it more amenable to streams.

In Q if I'm not miss-lead promise transitions from one state to another and nothing like progress notifications exist. KrisZ had forEach-able promises and then also accepts 3rd onProgress listener that may be called for each delivered chunk. 
 

 
4. I've started work on ES Harmony Proxy based promises, then-able promises is no go there for obvious reasons, Q-is better, but 1. It comes with lot's of stuff that is useless with Proxy based promises. 2. 'emit' method on promise objects get's in the way. This is also addressed by suggested proposal.

I really don't want to come up with yet another promise implementation that will be incompatible and will lead to even bigger fragmentation. All needed is just a minimal spec that can address all the points mentioned. I don't care whether it will be promise a/b/c or d as long all of this is addressed.

I'm with you there. But AFAICT it's both better and worse than you think. A and B are almost identical -- interoperable in every way -- but the opaqueness constraint is a fundamental incompatibility. Promises/A solves interop nicely but at the cost of opacity. Promises/B is arguably more correct but requires you to use The One True Promise Library. Oh, and you have to make sure your code gets the same One True Promise Library as the libraries you're exchanging promises with. To date this is an unsolved problem.

 
Well there is fundamental difference  in way promises are chained, mainly because of opacity. Unless Promise/A will respect that clause no interoperability will be possible.


Yes, I know -- I was trying to point out that the APIs themselves are completely compatible but was making the point about opacity. That said, a Promise/A implementation could extend the One True Promise constructor to be interoperable with a Promise/B implementation. But this does absolutely nothing for my uneasiness about this assumption that require("promise") will always give you the right library. There's nothing in SecurableModules that can give you this guarantee. This isn't just an academic concern: the possibility of this or any promise implementation ending up in node approaches 0. So you've got to path shift. Unless we come up with something better dependency hell for opaque promises is virtually assured.
 
Ok I see! I think that most of the nodejs community is on npm that sort of guarantees that require('promise') will return the same thing or throw regardless of combination of packages installed.

Quite the opposite AFAICT. The npm shims could lead to multiple versions of a promise lib being installed and used at any given time.
 
Also here can be something like
require('commonjs/promise') that will provide base promise API that will be used behind the scenes by both require('q') and require('promise') to construct promises.

I think some kind of agreement on this require path would be absolutely required. It still doesn't completely solve the problem but it'd at least be a start.

Kris Kowal

unread,
Nov 30, 2010, 2:54:09 AM11/30/10
to comm...@googlegroups.com
On Tue, Nov 23, 2010 at 4:34 PM, Dean Landolt <de...@deanlandolt.com> wrote:
>> Ok I see! I think that most of the nodejs community is on npm that sort of
>> guarantees that require('promise') will return the same thing or throw
>> regardless of combination of packages installed.
>
> Quite the opposite AFAICT. The npm shims could lead to multiple versions of
> a promise lib being installed and used at any given time.

This is probably the most compelling reason to go with a duck-type
under the hood, for a promises-level-0 spec (defining away the opacity
of the promise). Unfortunately, Q promises support methods beyond
promise.when/then, and even though these could be omitted for most
work getting done today, it would constitute a missed opportunity to
do promise pipelining between workers separated by high latency. Even
if those methods were omitted, we would still need a catchall. A proxy
could possibly fill that role eventually, we need a solution that
works today, such as a function(operator, …args) or a Promise instance
with an emit(operator, …args). Using the Q API directly affords the
ability to switch the implementation details when better solutions
become available.

I think it would be better to serialize and desrialized promises
across sandboxes than attempt to go with a duck-type. I'm mulling the
idea of a QSON notation, based on Tyler Close's ref_send protocol,
which could be used as an inter-worker or inter-sandbox communication
layer. It would work better if we got weak maps and proxies sooner
than later since there are garbage collection implications.

I could be convinced to define a duck-type, but it would have to be
more complete that the thenable feature subset for me to bite. Tyler
Close's API was that a promise was simply a function that received
messages. That precludes the possibility that a promise can be
resolved to a function, which wasn't a problem for him in practice.
It looked like

promise("when", resolved, rejected)

And isPromise(promise) would simply check whether the promise was a
function. It worked but I still prefer using full objects since they
open up the type domain for resolution and make it possible to have
useful toString inspection and valueOf behaviors.

Again, I'm entertaining the idea of using a duck-type. My requirement
is that there be a catch-all so you can construct remote promise proxy
objects that either forward or reject arbitrary messages, like
promise.emit("get", "x").

Kris Kowal

Reply all
Reply to author
Forward
0 new messages