On 12/03/13 23:32, Mark Miller wrote:
On Tue, Dec 3, 2013 at 2:06 PM, Peter Thiemann <[email protected] <mailto:[email protected]>> wrote:



    On 03.12.2013, at 22:09, Mark Miller <[email protected]
    <mailto:[email protected]>> wrote:

        ----------
        From: *Andreas Schlegel* <[email protected]
        
<https://mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]>>
        Date: Tue, Dec 3, 2013 at 6:33 AM
        To: Tom Van Cutsem <[email protected]
        
<https://mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]>>,
        "Mark S. Miller" <[email protected]
        <https://mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]>>
        Cc: Brendan Eich <[email protected]
        
<https://mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]>>,
        [email protected]
        
<https://mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]>


        Hello,

        yes you're right, the problem of the contracts researched by
        prof. Thiemann was, that the targets behind the proxies could
        not be compared by == and ===.

        How can I merge contracts of multiple proxies and represent
        them by just a single proxy, if I cannot compare them (or get
        the target)?

        We have discussed first an approach with new operators
        spezific to proxy comparism but this is not the way.
        If I can include a trap for asking the proxy if he's
        transparent or not, it is possible to use the same operators
        by asking the proxy.



    Yes, for JS or any language where identity is based on an
    equality *predicate*, it is too late: The conflict between
    contracts and identity can't be fixed. Note that, through my lack
    of foresight about this issue, E is among the languages that
    can't be fixed.

    Actually, I don't think it is too late. We have come up with a
    design where the proxy's handler can determine whether equality is
    opaque or transparent - this is what Andreas is currently trying
    to implement. This way, you may create proxies with different EQ
    semantics while still guaranteeing that EQ is an equivalence
    relation.


There is much here that I did not understand: What is "transparent" vs "opaque" equality? How can it be up to the handlers to determine this, on the one hand, but still guarantee that it is an equivalence relation, on the other hand? These two seem to be in conflict.

Transparent equality: EQ recursively dereferences proxies to their ultimate non-proxy targets and compares those. Opaque equality: EQ directly compares, i.e., the current state of affairs in JS.

Consider
var p = new Proxy ( target, handler);
The trick is *not* to trap equality, but to let the handler control whether EQ dereferences p to target before comparing pointers. Notice that one handler only controls one step of dereferencing. If target is a proxy, again, then EQ recursively asks that proxy's handler until either a non-proxy object is reached *or* the handler does not request further forwarding.
Here is an outline of the proposal:
http://proglang.informatik.uni-freiburg.de/projects/JSProxy/proxy.pdf

With this design each individual proxy can choose to be transparent or opaque, while guaranteeing that EQ implements a non-trivial equivalence relation - the latter is straightforward to check.


In any case, weakening EQ so that the only guarantee that remains is that it is an equivalence relation would break existing uses of EQ in JS (i.e., "===" aside from NaN). For example, an EQ that always said true would still be an equivalence relation, so clearly something more is needed. The position Tom and I take is that, aside from -0.0, JS === guarantees full observational equivalence, i.e., for any program containing the fragment

if (x === y) {
.... x ....
}

if all or any or all of the mentions of x in the body were changed to y, the meaning of the program would not change. (Also, given the usual simplifying assumptions about renaming, non-shadowing, and alpha equivalence.)

If the guarantees your === would provide is weaker than that, what are its guarantees, and why do you believe those to be sufficient?

Observational equivalence is hard to argue against, but I’ll give it a try, anyway. I believe such a strong spec is too limiting and excludes many useful applications. Examples that I am concerned about are wrappers for transparent monitoring and enforcing contracts. Apparently and unsurprisingly others are concerned about these examples, too.


Here is an example scenario. Then, I provide some code fragments to support a proposal for a weaker spec for ===. This leads to a question about join in the end.

~~~~~~

Consider a function sync(master,slave) that takes two objects and that wants these two objects to exchange information: the master syncing with the slave. For efficiency, the function sync first checks if these two objects are “the same”, in which case no synchronization is needed; perhaps a flag or timestamp needs to be set to indicate that synchronization has been achieved. If the objects are different, then some potentially expensive sequence of method calls may be needed to exchange the information. (Perhaps involving authentication, passing of capabilities, etc.)


If we put a contract on sync that imposes a Master interface on master and a Slave interface on slave and this contract is implemented using opaque proxies, then the sync function is losing the efficient shortcut.


(Why would an object implement the Master and the Slave interface? Here are two possible reasons: the master may be elected at run time or there may be a hierarchical system where level-n masters are slaves on level-(n+1))

~~~~~~

A proposal for a weaker spec for ===


In the following code fragments, I am using Java interface notation to indicate a dynamically checked contract for brevity.

Let’s say we have a function that can treat “same” objects more efficiently, as motivated in the sync example:


function f (x, y) {

...

if (x === y) {

… x.foo(); x.inc(); y.inc(); z = y.bar() …

}

…

}


Using contracts, x and y may be restricted to certain roles.

Let’s say,

interface Role1 {

foo();

inc();

}

interface Role2 {

bar();

inc();

}


var g = … apply contract( (Role1, Role2) -> Any ) to f;


var obj = {

state: 42;

foo: function () { this.state = 0; };

bar: function () { return this.state;};

inc: function () { this.state++; };

}


g (obj, obj);


I want to argue that this call of g should execute the code guarded by (x === y) in f, because the interposition of the contract may otherwise change the semantics. In other words, I want to make sure that g(obj, obj) either raises an error due to a contract violation or it has exactly the same effect and result as f(obj, obj).


What guarantees can we assume below (x===y)?

If x===y, then uses of x and y *in their common interface* (i.e., in the intersection of their roles) are interchangeable.


In the example, the common interface is

interface Role1&Role2 {

inc();

}

therefore

if (x === y) { … x.foo(); x.inc(); y.inc(); z = y.bar() … }

must be observationally equivalent to each of the following

if (x === y) { … x.foo(); x.inc(); x.inc(); z = y.bar() … }

if (x === y) { … x.foo(); y.inc(); y.inc(); z = y.bar() … }

if (x === y) { … x.foo(); y.inc(); x.inc(); z = y.bar() … }


That is, sameness of x and y is reduced to the ability to observe the same state changes through both x and y. Of course, limited to the common interface.


The extreme case for the above f-example would arise if the interfaces Role1 and Role2 were disjoint. In this case, no transformation would be sanctioned from the knowledge that x === y.

~~~~~~

This observation leads me to a question about join:

If only join is provided, but not EQ, then I don’t see how to implement the function f above. If I successfully obtain z = join(x, y), then it fulfills interface Role1&Role2, but I cannot use it in lieu of x or y in the body of the conditional because it does not implement foo() and bar().

If Role1&Role2 were empty, then the successful join would produce a useless object. For example, in the Master/Slave example above, it is highly likely that the interfaces are disjoint.



--
Cheers,
--MarkM

-Peter


_______________________________________________
dev-tech-js-engine-internals mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-tech-js-engine-internals

Reply via email to