[E-Lang] Promises, equality and trust

Mark S. Miller markm@caplet.com
Fri, 27 Jul 2001 00:36:14 -0700


These are great questions.  I'll give the quick answers in this message, and 
the longer ones in later messages.

At 02:54 PM Thursday 7/26/01, Mark Seaborn wrote:
>I have some questions about promises.
>
>It seems that promises are given special treatment in E.  When they
>resolve, promises *become* the reference they resolved to, rather than
>just being proxies.  

Correct.

>This has the effects that:
>
> * Immutable circular data structures can be created.  (Though I
>   wonder if this is a hazard, since it means that when traversing a
>   data structure you have to worry about whether it contains cycles.)

Yes, it does mean cycles of immutable data can be created, and it does mean 
that general traversal algorithms must deal with the resulting hazard.  
Since it's at least as hard to prevent cycles as to live with them, it's 
better to live with them because they're more expressive.  In fact, in the 
context of the overall E architecture, I don't think we had a choice.  I 
don't believe we could prevent cyclic data.

It's not all that hard to write traversal algorithms that are cycle 
tolerant, but you need some good examples and some practice.  When people 
don't, because they assume they won't encounter a cycle, or because they 
forgot about the issue, the consequences are normally fail-stop: stack 
exhaustion.

<An aside on VirtualMachineErrors, replay, and persistence>
Note: Stack exhaustion results in the throwing of a 
java.lang.OutOfMemoryError, which, in the current implementation, would 
unwind the stack and run finally clauses (E for unwind-protect) as you'd 
expect.  This is a bug. Instead, from the point of view of E-language code, 
the occurrence of any java.lang.VirtualMachineError must terminate the 
vat-incarnation before any further E-language code gets to run, since the 
computation state following such an error is non-replayable (and has other 
fatal problems as well).  If the vat is persistent, this is a vat-crash that 
may very well result in the automatic revival from previous checkpointed 
state.  Speculating now, part of configuring a vat's persistence may be to 
say how many time the same revival should be attempted in the face of 
repeated crashes that prevent us from reaching the next checkpointable state.
</An aside on VirtualMachineErrors, replay, and persistence>


> * Once resolved to an object, a promise stays resolved to that
>   object.  

Correct.

>This is useful for efficiency in terms of cacheing, and in
>   terms of reducing latency (if a resolved promise is passed to
>   another vat, the other vat gets the object the promise resolved to,
>   not a proxy).  It also seems useful from a correctness point of
>   view -- the object a promise is resolved to can't change underneath
>   you.  

All correct.

>This advantage seems to be thrown away by using the Miranda
>   method whenResolved for detecting resolution, 

Very perceptive, and more correct than not.  This is an ugly issue in the 
language, and I'm not at all confident I've ended up in the right place.  I 
believe the behavior seen by the "normal" programmer when interacting with 
"normal" programs is correct.  Here, I define a "normal" program as one that 
never uses the experimental Plumbing expression, and that never overrides 
whenMoreResolved and whenBroken.  (As of the next release, the old 
"whenResolved" message has transmuted into "whenMoreResolved" for reasons 
I'll be explaining.)  In any case, I'd consider a change here that preserves 
this "normal" behavior, and so would be "practically upwards compatible."  
If we figure out such an improvement, it should be relatively painless to 
shift to it, so it's good you're raising this issue now.  I'll postpone 
further discussion of this topic to another message.


>(Incidentally, what is the origin of the term `Miranda method'?
>Presumably it doesn't come from the language Miranda.)

In the U.S., one's Miranda rights include "If you cannot afford a 
lawyer, one will be provided for you".  They're named after this case:
http://www.thecapras.org/mcapra/miranda/rights.html .

If you don't provide a Miranda method from yourself, one will be provided 
for you.  


>Does a holder of a promise always get told about the promise's
>resolution, or does this information only come through the call to an
>observer passed to a whenResolved message, with the local proxy for
>the promise wrapping the observer passed in whenResolved so that it
>detects the resolution too?  (Giving whenResolved special treatment in
>this way does not seem very elegant.)

The latter.  The holder of a promise never gets told simply by virtue of 
holding. Rather, the holder of a promise can arrange to find out by means of 
the various "when..." constructs you already know about.  The holder of a 
promise can also check (ie, poll) by use of "Ref isResolved(expr)".

Your description of the implementation is essentially correct.  It doesn't 
seem very elegant to me either, but I've never come across or been able to 
think up anything better.  Suggestions would be great.


>Presumably promise references are tagged as being promises when they
>are sent in messages.  This would mean that promises cannot be
>implemented in the E language.  Is that right?

It used to simply be right.  Now it's half right.  The integrity of the 
state transition diagrams on 
http://www.erights.org/elib/concurrency/refmech.html are still guaranteed by 
the language kernel, but the comm system that stretches capabilities between 
address spaces no longer need be in the kernel -- these can now be written 
in normal E.  In fact, eventually I expect to replace the Java-written comm 
system with one written in E and given least authorities, so we can analyze 
our risks from the possibility of comm system bugs.  By the same token, it 
should be possible to have several comm systems of different design 
coexisting in the same vat.  Multiple coexisting comm systems of course 
raises various new complexities, which I'll postpone thinking about for now.

In order to do this, it needed to be possible for non-privileged code to 
supply the encoding and forwarding over the wire behavior behind an eventual 
ref.  This is provided by 
http://www.erights.org/elib/concurrency/refmech.html .  See 
http://www.eros-os.org/pipermail/e-lang/2001-April/005226.html , but keep in 
mind that Redirector was renamed ProxyResolver and RefHandler was renamed 
ProxyHandler.


>If the invocation of observers passed in whenResolved messages is the
>way promises get resolved to values, wouldn't that mean promises can
>get resolved to different values in different vats?  A promise that
>started out being equal to itself would end up splitting into
>inconsistent values.

Yes, but I believe this issue can't be fixed.  The rationale for the current 
design rests on that unproven belief, so there's definitely something to 
talk about.  More later.


>Another concern I have:  If Alice passes to Carol her reference to Bob,
>Carol's vat will have to establish a connection to Bob's vat in order
>to send messages to Bob (assuming all three live in different vats).
>Assuming Bob and Bob's vat are in collusion, Bob will be able to
>distinguish between messages from Alice and messages from Carol.
>
>If Bob doesn't want Alice to delegate Bob's authority to Carol,
>obviously Bob will not be able to stop this, but if Alice's default
>behaviour is to leave Carol to establish a new connection with Bob,
>Bob will be able to recognise this, and silently let Carol's reference
>to Bob do something different to Alice's reference to Bob.
>
>Should the default behaviour be to provide proxies to delegate
>authority unless we explicitly trust vats to play nice and ensure that
>a reference works the same regardless of what connection it is used
>through?
>
>I'm assuming the network is not anonymous, which I think is
>reasonable, but even if that's not the case, the
>connection-orientedness of E's protocol could lead to different
>behaviours of the same reference over different connections.


Fascinating.  What you're proposing is what E-Speak2.2 planned to do: have 
the default case be not to shorten paths (the case actually implemented), 
and then have a path shortening Granovetter introduction be something a 
program can explicitly request (AFAIK, their plan for this was sound, but it 
never got implemented).  I remember arguing with Alan over this years ago 
(remember Alan?), but yours is the first rationale for the E-Speak position 
I understand.

I still think default shortening is the right engineering decision, because 
the price of not shortening is too high, and the above price of shortening 
will IMO usually not be a practical issue.  Note that the issue is only 
defaults.  As in the planned E-Speak2.2, in E both are possible. It's just 
that the default is on the other foot.  Alice can always easily introduce 
Bob to a forwarder for Carol running in VatA.  Using the experimental 
PlumbingExpr for a moment,

    bob <- foo(def _ delegate { carol })

would do it.  The reason the PlumbingExpr is considered experimental, 
discouraged for now, and not even present in the language by default, is 
because of the need to settle exactly the kinds issues you raise earlier: 
What does it mean when an object overrides or doesn't implement Miranda 
methods involved in reference resolution?


        Cheers,
        --MarkM