Mark S. Miller (2013-06-21T06:13:12.000Z)
On Thu, Jun 20, 2013 at 3:25 PM, Forbes Lindesay <forbes at lindesay.co.uk>wrote:

> Returning to Mark Miller's comment:
>
> > Any beginning JavaScript programmer learns the meaning of `a.foo(b,c)`,
> i.e., synchronous message send, long before they learn about callbacks.
>
> I don't see any simple and obvious path from understanding the synchronous
> message send to understanding how promises work.  How does that explanation
> look?
>

Take it in three steps.


Step 1

var d = a.foo(b,c);

This does synchronous message sending. It delivers the message "foo(b,c)"
to the object designated by "a" immediately, transferring control to that
object -- the callee -- now. The caller blocks waiting for the callee to
respond. The callee runs to completion, finally returning a value. At this
point the caller receives that value into d and continues.

In order for the callee to be invoked synchronously, it must be local,
since we don't want to wait on a round trip to a remote object.

In a communicating event-loop system, such synchronous call/return has a
strong side effect contract with pros and cons.
* pros: no local time passes between the calling and the being called, so
the callee gets control in the state in which the caller made the request.
* cons: the callee runs while the caller, and the caller's caller, etc, are
suspended in the midst of some operations. They might have suspended
invariants, or otherwise be unprepared for recursive entry in their current
state. This threatens both caller and callee.


Step 2

var dP = aP ! foo(b,c);

This does asynchronous message sending. It delivers the message "foo(b,c)"
to the object designated by aP eventually, eventually causing that object
-- the callee -- to gain control. But the caller proceeds now without any
interleaving of control by others. Since the caller proceeds immediately,
dP cannot yet provide access to what the callee will return. But it still
designates that value, whatever it will be. A designator whose designation
is not yet determined is a promise. If the callee eventually returns an
int, then dP is already a promise for that int, though neither it nor we
know that yet.

Since we're only sending a message to the callee eventually and not waiting
for it to respond, we don't much care whether it is local or remote.

In a communicating event loop system, such asynchronous message sending has
a strong side effect contract with the opposite pros and cons.
* pros: The caller executes to completion without possibility of
interference from the callee. Any delicate state the caller was in the
midst of manipulating is unperturbed, and the caller can complete its
manipulation, confident that its invariants were not disrupted. Likewise,
the callee receives the message in an empty stack state, in which all
previous turns have presumably restored all heap invariants. This is a
robust situation from which to start running.
* cons: Between the caller requesting and the callee receiving, and
arbitrary number of previously queued turns may run in the meantime,
changing the world from the one in which the caller decided to send the
message. By the time it arrives, it may no longer be relevant or
appropriate.


Step 3

Ok, so we can "." on local objects like "a", and we can "!" on local or
remote objects like aP. What about dP? It also designates something, but
that something is separated from us in time, not (necessarily) in space. No
matter! Asynchrony handles that too. If aP is remote, the message gets
queued on the event-loop hosting the object aP designates. If dP is
pending, the message gets queued in dP itself. Once dP knows what it
designates, it forwards all these queued messages to that target using !.




>
> It seems on the other hand I can see a natural progression from callbacks
> to `.done` and from `.done` to `.then` because if you write any code using
> `.done` it's infinitely obvious that you need `.then`.
>
> I think it's interesting to also reflect on Tasks in C# which have been
> around for a while.  They don't have any methods for automatically tracking
> unhandled rejections even though the language supports handling object
> destruction.  All the methods that are built in to the Task object match up
> with `.done` rather than `.then`.  This becomes much less of a problem once
> the language has `await` or similar.
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>



-- 
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130620/47651d18/attachment.html>
github at esdiscuss.org (2013-07-12T02:27:37.435Z)
> I don't see any simple and obvious path from understanding the synchronous
> message send to understanding how promises work.  How does that explanation
> look?

Take it in three steps.


## Step 1

```js
var d = a.foo(b,c);
```

This does synchronous message sending. It delivers the message `foo(b,c)`
to the object designated by `a` immediately, transferring control to that
object -- the callee -- now. The caller blocks waiting for the callee to
respond. The callee runs to completion, finally returning a value. At this
point the caller receives that value into d and continues.

In order for the callee to be invoked synchronously, it must be local,
since we don't want to wait on a round trip to a remote object.

In a communicating event-loop system, such synchronous call/return has a
strong side effect contract with pros and cons.

* pros: no local time passes between the calling and the being called, so
the callee gets control in the state in which the caller made the request.
* cons: the callee runs while the caller, and the caller's caller, etc, are
suspended in the midst of some operations. They might have suspended
invariants, or otherwise be unprepared for recursive entry in their current
state. This threatens both caller and callee.


## Step 2

```js
var dP = aP ! foo(b,c);
```

This does asynchronous message sending. It delivers the message `foo(b,c)`
to the object designated by aP eventually, eventually causing that object
-- the callee -- to gain control. But the caller proceeds now without any
interleaving of control by others. Since the caller proceeds immediately,
dP cannot yet provide access to what the callee will return. But it still
designates that value, whatever it will be. A designator whose designation
is not yet determined is a promise. If the callee eventually returns an
int, then dP is already a promise for that int, though neither it nor we
know that yet.

Since we're only sending a message to the callee eventually and not waiting
for it to respond, we don't much care whether it is local or remote.

In a communicating event loop system, such asynchronous message sending has
a strong side effect contract with the opposite pros and cons.

* pros: The caller executes to completion without possibility of
interference from the callee. Any delicate state the caller was in the
midst of manipulating is unperturbed, and the caller can complete its
manipulation, confident that its invariants were not disrupted. Likewise,
the callee receives the message in an empty stack state, in which all
previous turns have presumably restored all heap invariants. This is a
robust situation from which to start running.
* cons: Between the caller requesting and the callee receiving, and
arbitrary number of previously queued turns may run in the meantime,
changing the world from the one in which the caller decided to send the
message. By the time it arrives, it may no longer be relevant or
appropriate.


## Step 3

Ok, so we can `.` on local objects like `a`, and we can `!` on local or
remote objects like aP. What about dP? It also designates something, but
that something is separated from us in time, not (necessarily) in space. No
matter! Asynchrony handles that too. If aP is remote, the message gets
queued on the event-loop hosting the object aP designates. If dP is
pending, the message gets queued in dP itself. Once dP knows what it
designates, it forwards all these queued messages to that target using `!`.