[Promises] Promises in Narwhal

42 views
Skip to first unread message

Kris Kowal

unread,
May 31, 2009, 9:04:18 PM5/31/09
to serv...@googlegroups.com
If you want to check out promises and how they work, how to work with
them, I've ported Tyler Close's ref_send API, by way of Caja's port,
to Narwhal as a ServerJS module "promise" (it depends on a
factored-out require("reactor").enqueue that varies from platform to
platform). Here's how to get started with promises in Narwhal. For
the time being, this is in my branch and hasn't landed in Tom's repo.

kris@imoo:~/foo $ git clone git://github.com/kriskowal/narwhal.git
Initialized empty Git repository in /Users/kris/foo/narwhal/.git/
remote: Counting objects: 2884, done.
remote: Compressing objects: 100% (1291/1291), done.
remote: Total 2884 (delta 1802), reused 2463 (delta 1486)
Receiving objects: 100% (2884/2884), 2.28 MiB | 1094 KiB/s, done.
Resolving deltas: 100% (1802/1802), done.

kris@imoo:~/foo $ cd narwhal/

kris@imoo:~/foo/narwhal master $ bin/sea
PATH=/Users/kris/foo/narwhal/bin:/bin:/usr/bin
SEA=/Users/kris/foo/narwhal
SEALVL=1

kris@imoo:~/foo/narwhal master $ narwhal
Rhino 1.7 release 3 PRERELEASE 2009 04 05
js> var Q = require("promise");

You might find this example illustrative of the basic principles.

1
2 var Q = require("promise");
3
4 print('resolve immediately');
5 Q.when(10, function (a) {
6 print('first resolved '+ a);
7 });
8
9 var {promise:promise, resolve:resolve} = Q.defer();
10 Q.when(promise, function (a) {
11 print('second resolved ' + a);
12 });
13 print('enqueue resolution');
14 resolve(20);
15
16 print('end of first event');
17

This example illustrates multi-stage deference:

1
2 var Q = require('promise');
3
4 var {promise:a, resolve:aResolve} = Q.defer();
5 var {promise:b, resolve:bResolve} = Q.defer();
6
7 var foo = function (a, b) {
8 return Q.when(a, function (a) {
9 print('a resolved');
10 return Q.when(b, function (b) {
11 print('b resolved');
12 return a + b;
13 });
14 });
15 };
16
17 Q.when(foo(a, b), function (result) {
18 print(result);
19 });
20
21 print('resolve b');
22 bResolve(3);
23
24 print('resolve a');
25 aResolve(2);

The implementation appears to have a weakness in variadic arguments
(max 3), which I don't understand as yet and I'm still figuring out
the API.

Kris Kowal

Kris Zyp

unread,
May 31, 2009, 9:16:16 PM5/31/09
to serv...@googlegroups.com
What would need to be done to upgrade this to the proposed promise API
[1] or does it use it underneath? Or would I be able to create another
branch to implement that?
Kris
[1] http://groups.google.com/group/serverjs/msg/967367b7826aaf04?

Kris Kowal

unread,
May 31, 2009, 11:18:10 PM5/31/09
to serv...@googlegroups.com
On Sun, May 31, 2009 at 6:16 PM, Kris Zyp <kri...@gmail.com> wrote:
>
> What would need to be done to upgrade this to the proposed promise API
> [1] or does it use it underneath? Or would I be able to create another
> branch to implement that?
> Kris
> [1] http://groups.google.com/group/serverjs/msg/967367b7826aaf04?

I don't have strong opinions about how promises should work, but I
have inklings about what they're supposed to be like and how they can
be used in a generic event system and made simple and usable. So,
nothing depends on promise.js and you're welcome to fork/expierment
whatever. May the best fork win! :-)

Kris Kowal

Kris Zyp

unread,
May 31, 2009, 11:27:11 PM5/31/09
to serv...@googlegroups.com

I was kind of hoping they both would win. Like I had mentioned earlier,
they aren't mutually exclusive. There is a proposal for the API for the
promises themselves (which I referenced) and the API for creating and
routing promises (the "Q" API which you implemented). Is that not the
route you would like to see this go?
Kris

Kris Kowal

unread,
Jun 1, 2009, 12:08:28 AM6/1/09
to serv...@googlegroups.com
On Sun, May 31, 2009 at 8:27 PM, Kris Zyp <kri...@gmail.com> wrote:
> I was kind of hoping they both would win.

Oh, I see.

> Like I had mentioned earlier,
> they aren't mutually exclusive.

My apologies, I have not yet begun to discuss promises myself, being
not yet qualified, which is a problem for which I suspect many of us
have refrained from the discussion. But, given that I've come to
believe they're the right road to solving perennial problems, I'm
beginning to do my part to learn about them and in turn teach others,
as we must rapidly do given that unlike recursion, pointers, and
object-oriented programming, promises are not the bread and butter of
every fresh college grad.

> There is a proposal for the API for the
> promises themselves (which I referenced) and the API for creating and
> routing promises (the "Q" API which you implemented). Is that not the
> route you would like to see this go?

I think I would. I would like to see a more friendly face on the Q
API, and if that can be done with an abstraction layer, I'm all for
it. Given a quick glance, I actually prefer "when" over
"addCallbacks" though.

If you'll forgive a digression, I've been developing a "Codish"
lexicon on the side to help me keep names for ideas straight, and to
avoid mixing metaphors. On Planet Kris Kowal (Population: 1), the
words associated with events are:

- event
- observe
- signal
- send
- when (before, after)
- observable
- observer
- state
- promise
- binding
- break/reject/revoke
- reactor

"event" is an object that gets passed as the context to a chain of
observers. "event" usually implements the usual stuff for stopping
propagation and canceling the primary action.

"observe(name, observer)" gets a "signal" and adds an observer, with
"signal.when(observer)".

A "signal" is an object that can be observed when it is "sent" an
"event". In my present idealization of signals, they receive the
"event" as a context object and the arguments of the call to "send",
like "signal.send.apply(event, args)". A "signal" has an "action"
function that may or may not do anything, but is a fixed point around
which other signals get sent.

"when" is a function that attaches an observer to a signal and returns
a new "signal". I've also implemented in the past "before" and
"after" so that I can selectively attach events around particular
signals with very fine control. The context signal is the action of
the returned signal.

"signaler" is an object that implements "observe(name, observer)" and
"signal(name, event, args)".

An "observable" is a kind of signaler whose methods calls can be
observed by name. So, if I have an observable that has a "foo"
method, and I "observable.observe('foo', function (a, b, c) {})",
"observable.foo(1, 2, 3)" will cause an event to be sent to the "foo"
observer with itself as the context object and my arguments in place.
For an "observable", the original member function becomes the "action"
of the signal.

A "state" is a type of "signal" that begins unset, and then becomes
set when it is "sent" an "event", and remains set, such that any
"observers" that are registered after it is set are immediately sent
the "event". States are handy for capturing certain events on or
after their occurrence and sending the event to all observers exactly
once. In particular, I've had need of states for "onload" and
"DOMContentReady" in the past, to avoid certain races or
multiple-dispatch errors.

A "promise" is a type of "signal" that is initially "unresolved" and
is then either "resolved" with a "value" or "broken" with an error or
exception (not sure which yet). With a "promise", "when" is extended
to accept both a callback and an errback.

A "binding" is a type of "signal" that always has a value but can be
observed for changes to that value.

A "reactor" is an event loop. It would make sense in JavaScript for
this to implement "enqueue" or "defer" and "setTimeout" and
"setInterval" if timers are available.

This and other madness at https://cixar.com/svn/codish/defs/

I'm not recommending that all of this be crammed into the promise or Q
API, but rather that this is the ecosystem of nomenclature that I
would graze for these API's. I imagine actually that we could have a
four-layer architecture, not necessarily in this order:

- event
- promise
- q
- reactor

Kris Kowal

Kris Zyp

unread,
Jun 1, 2009, 9:08:54 AM6/1/09
to serv...@googlegroups.com

Kris Kowal wrote:
> On Sun, May 31, 2009 at 8:27 PM, Kris Zyp <kri...@gmail.com> wrote:
>
>> I was kind of hoping they both would win.
>>
>
> Oh, I see.
>
>
>> Like I had mentioned earlier,
>> they aren't mutually exclusive.
>>
>
> My apologies, I have not yet begun to discuss promises myself, being
> not yet qualified, which is a problem for which I suspect many of us
> have refrained from the discussion. But, given that I've come to
> believe they're the right road to solving perennial problems, I'm
> beginning to do my part to learn about them and in turn teach others,
> as we must rapidly do given that unlike recursion, pointers, and
> object-oriented programming, promises are not the bread and butter of
> every fresh college grad.
>
>
>> There is a proposal for the API for the
>> promises themselves (which I referenced) and the API for creating and
>> routing promises (the "Q" API which you implemented). Is that not the
>> route you would like to see this go?
>>
>
> I think I would. I would like to see a more friendly face on the Q
> API, and if that can be done with an abstraction layer, I'm all for
> it. Given a quick glance, I actually prefer "when" over
> "addCallbacks" though.
>

I certainly prefer shorter syntax as well, but my general intent is to
try to preserve maximum compatibility and AFAICT, the majority of
existing code in JavaScript that uses promises is written using Dojo's
promises, which use addCallback(s). I suppose it is true that
implementors could alias Dojo's functions if they wanted to preserve
compatibility though. However, if you like four letter words, wouldn't
"then" make more sense when used as a method name on a promise? "when"
works grammatically as a static function where the promise comes after
the function name (as in Q), but when the function name comes after the
promise, it seems like "then" reads better.
Kris

Kris Kowal

unread,
Jun 1, 2009, 3:04:09 PM6/1/09
to serv...@googlegroups.com
On Mon, Jun 1, 2009 at 6:08 AM, Kris Zyp <kri...@gmail.com> wrote:
> I certainly prefer shorter syntax as well, but my general intent is to
> try to preserve maximum compatibility and AFAICT, the  majority of
> existing code in JavaScript that uses promises is written using Dojo's
> promises, which use addCallback(s). I suppose it is true that
> implementors could alias Dojo's functions if they wanted to preserve
> compatibility though. However, if you like four letter words, wouldn't
> "then" make more sense when used as a method name on a promise? "when"
> works grammatically as a static function where the promise comes after
> the function name (as in Q), but when the function name comes after the
> promise, it seems like "then" reads better.

Ah, I see. Perhaps we could do the one we like and support the one
that's backward compatible.

Reading like English isn't all that important to me. As I've
mentioned, I think that "Codish" is or should be only loosely inspired
by "English"; Codish should be a language where a single word has a
single meaning, a single antonym for each dimension of its meaning, a
single grammatical function ("verb", "noun"), correspond to a single
class of names (as "play", "pause", "resume", "stop" are orthogonal
with "begin" and "end"), with no mixed metaphors and no synonyms.

I think "when" has the proper meaning, and we're accustomed to knowing
that "eq(a, b)" and "a.eq(b)" are equivalent, apart from a potential
distinction in their polymorphic behavior, so I don't think that
Q.when(promise, observer) being equivalent to promise.when(observer)
at a higher level of abstraction is that much of a stretch.

Kris Kowal

Eugene Lazutkin

unread,
Jun 1, 2009, 11:21:17 PM6/1/09
to serverjs
Inline

On Jun 1, 8:08 am, Kris Zyp <kris...@gmail.com> wrote:
>
> I certainly prefer shorter syntax as well, but my general intent is to
> try to preserve maximum compatibility and AFAICT, the  majority of
> existing code in JavaScript that uses promises is written using Dojo's
> promises, which use addCallback(s). I suppose it is true that
> implementors could alias Dojo's functions if they wanted to preserve
> compatibility though. However, if you like four letter words, wouldn't
> "then" make more sense when used as a method name on a promise? "when"
> works grammatically as a static function where the promise comes after
> the function name (as in Q), but when the function name comes after the
> promise, it seems like "then" reads better.

Dojo's promises came from MochiKit (http://www.mochikit.com/) ---
Python-inspired JavaScript library by Bob Ippolito (http://
bob.pythonmac.org/). So in a sense promises are standing on the
shoulders of giants. :-)

Eugene

> Kris

Mark S. Miller

unread,
Jun 2, 2009, 12:20:27 AM6/2/09
to serv...@googlegroups.com, Brian Warner, Tom Van Cutsem, E. Dean Tribble
On Mon, Jun 1, 2009 at 8:21 PM, Eugene Lazutkin <eugene....@gmail.com> wrote:
Dojo's promises came from MochiKit (http://www.mochikit.com/) ---
Python-inspired JavaScript library by Bob Ippolito (
http://bob.pythonmac.org/). So in a sense promises are standing on the
shoulders of giants. :-)

Just to be clear about the history of ideas here, Dojo & Mochikit's Deferreds come from Twisted's Deferreds, which are an adaptation of E's promise architecture. Tyler's ref_send library is also an adaptation of the E architecture. E's promises were in turn inspired by Original-E at Electric Communities, Joule at Agorics, the concurrent logic programming languages, especially Flat Concurrent Prolog and Vulcan, and Actors. The promise pipelining notion was first invented by Lubia Shrira and Barbara Liskov in 1988 and then independently by E. Dean Tribble and myself at Xanadu Operating Company in 1989.

A good explanation of the history of these ideas is section 11 ("From Objects to Actors and Back Again") of <http://www.erights.org/talks/promises/paper/tgc05.pdf>. In searching for this paper, I stumbled across a truly great set of slides from Tom van Cutsem at <http://prog.vub.ac.be/~tvcutsem/teaching/csse/CommunicatingEventLoops.pdf>. I recommend reading these slides along with the paper.

Brian Warner was one of the creators of Twisted Python. At <http://www.lothar.com/blog/twisted/Twist_E-2005-05-27-18-00.html> he writes about the relationship of Twisted's Deferreds and E's promises.

--
   Cheers,
   --MarkM

Mark S. Miller

unread,
Jun 2, 2009, 1:27:37 AM6/2/09
to ServerJS


---------- Forwarded message ----------
From: Brian Warner <war...@lothar.com>
Date: Mon, Jun 1, 2009 at 9:43 PM
Subject: Re: [serverjs] Re: [Promises] Promises in Narwhal
To: "Mark S. Miller" <eri...@google.com>
Cc: serv...@googlegroups.com, Tom Van Cutsem <tvcu...@vub.ac.be>, "E. Dean Tribble" <dtri...@gmail.com>



On Mon, 1 Jun 2009 21:20:27 -0700
"Mark S. Miller" <eri...@google.com> wrote:

> On Mon, Jun 1, 2009 at 8:21 PM, Eugene Lazutkin
> <eugene....@gmail.com>wrote:
>
> > Dojo's promises came from MochiKit (http://www.mochikit.com/) ---
> > Python-inspired JavaScript library by Bob Ippolito (
> > http://bob.pythonmac.org/). So in a sense promises are standing on
> > the shoulders of giants. :-)
> >
>
> Just to be clear about the history of ideas here, Dojo & Mochikit's
> Deferreds come from Twisted's Deferreds, which are an adaptation of
> E's promise architecture.

Bob has been very involved with Twisted in the past, and I think he's
familiar with E as well, so no surprises there.


> Brian Warner was one of the creators of Twisted Python. At <
> http://www.lothar.com/blog/twisted/Twist_E-2005-05-27-18-00.html> he
> writes about the relationship of Twisted's Deferreds and E's promises.

I should disclaim some credit here: I've contributed to Twisted but am in no
way a creator of it, having first learned about it several years after its
genesis. Glyph Lefkowitz (the genuine creator of Twisted) came up with
Deferreds. Zooko and I asked Glyph about his inspirations at the most recent
PyCon: I don't remember the details but I think Deferreds were less directly
inspired by E's promises than I'd previously believed: I seem to recall that
Glyph was aware of E's promises but wasn't specifically trying to recreate
them in Python.

I *am* responsible for Foolscap, which is my attempt to bring ideas from E
into the world of Python, specifically atop Twisted. So far I've only managed
(local) eventual-sends and RPC-style remote calls with ocap security
properties, and local Promises. But remote Promises and full
promise-pipelining is on the roadmap.

cheers,
 -Brian



--
   Cheers,
   --MarkM
Reply all
Reply to author
Forward
0 new messages