[e-lang] "Objects as Capabilities in Squeak"

Mark S. Miller e-lang@mail.eros-os.org
Sun, 21 Apr 2002 17:37:06 -0700


At 11:03 PM 4/16/2002 Tuesday, Rob Withers wrote:
>I must note that I am still exploring this mechanism locally and I
>wonder if it makes sense to explicitly use eventual sending in the local
>vat.  I must believe that the asynchronous nature of eventual sends is
>beneficial locally.

They are, for many of the same reasons that people often wish to turn to 
concurrency in the local case -- because we often set up actions now to 
happen on the occurrence of events that may happen in the future.  In a 
classic thread-concurrency paradigm, these contingent future actions might 
be represented by threads blocked on the occurrence of that future 
condition.  In E, this is often simply a message eventually sent to a 
promise that will resolve on the occurrence of that future condition.  The 
other E pattern is a when-catch, in which the right side (the 'done' 
function) will be eventually invoked one a given reference resolves.  (The 
when-catch is actually implemented in terms of an eventually sent 
whenMoreResolved message.)

Walnut has many good examples of E concurrency patterns, many of which are 
useful locally.  http://www.erights.org/elang/concurrency/race.html has a 
few more.  http://www.erights.org/elib/concurrency/turns.html argues that, 
even when future events are not an intrinsic concern, that event-loop 
concurrency is better able to maintain state invariants (and thereby program 
correctness) than is classic purely sequential programming.

But my favorite simple-but-mind-twisting example of use of eventual sends, 
adapted from a Flat Concurrent Prolog example by Udi Shapiro, is the 
optimistic queue, reproduced below in E.


>Often,
>UI programming seems to result in views that know how to query the model
>(fair enough) but also models that know how to query views.

I learned the MVC paradigm first from Smalltalk-80, and then from some other 
pre-Morphic systems, so this surprises me.  Back then, the dogma was that the 
model didn't even know about the existence of views, per se, but saw them 
merely as yet more dependents on model state.  This is still how I think 
about how UI systems should be structured.  What am I missing?

Btw, although I always learned that one should also separate the view and 
controller, I never took that to heart, since, classically, the view and 
controller do know about each other as view and controller.  In the following 
I'll use "view" to represent both roles, regardless of whether they are 
represented by one object or two.

>This is
>probably the biggest detriment of Smalltalk programming, since it is
>done in a graphical environment.  A little more rigor seems to result in
>a model that is eventful and a view that responds to the model's events
>with message sending to the model. 

If the interactions between the view and the model are strictly 
asynchronous, then they can be in separate vats.  CapDesk, for example, uses 
this in order to graphically manipulate a remote file system.

CapDesk also makes use of a degenerate form of Chip's Unum pattern, used at 
Electric Communities to build, in Original-E, a shared decentralized 
graphical social virtual reality.  (The degenerate form used by CapDesk is 
the Summoner pattern, but I'll speak here of the full Unum pattern.)  It is 
often too hard or impossible for the view to do its job without synchronous 
access to some model-state.  However, if the view were otherwise happy to 
interact with the model asynchronously, it should be happy to make 
synchronous use of stale model-state, so long as that stale model-state is 
always a best efforts attempt at being less stale.

We achieve this by eventual-replication of the view-relevant portion of the 
model-state from the real model to each of these model-state-caches.  Each 
of these replicas, including the original, is considered a *presence* of the 
Unum.  Jointly they constitute the Unum as a whole: a single conceptual 
object with a distributed implementation -- one presence per referencing 
vat.  The authoritative replica, at the real model, is the *master presence* 
of the Unum, and the rest are *shadow presences*.  Each of the shadow 
presences registers as a dependent on the master presence, so, as the 
master's state changes, it eventually updates all its shadows.

Since all presences have the same external interface (which is considered to 
be the external interface of the Unum itself), a view would just register 
with and interact with the presence in its local vat, to which it has 
synchronous access.  If the model is in the same vat, then the view happens 
to be synchronously interacting with the real model-state, but it doesn't 
know that.  Should the presence be a shadow presence, it can satisfy 
staleness-tolerant queries immediately, but can only perform mutate 
operations eventually.  Therefore, since all presences have the same 
contract, all mutate operations should take effect only eventually.

If, as in classic MVC, the view only updates itself driven by notification, 
and not by its knowledge that it has just performed a mutate, everything 
should just work.  Since the Morphic paradigm sounds different, I don't know 
how this applies.

Note: Besides CapDesk's Summoner pattern, the current progress towards 
reviving the full Unum pattern can be found in the Lamport cell at 
e/src/esrc/org/erights/e/elib/slot/ in the files

    LamportSlotMaker.emaker
    lamport.emaker
    EverFormulaMaker.emaker
    ever.emaker

See also the taxonomy diagram at 
http://www.erights.org/elib/equality/same-object.html for how Una relate to 
other categories of E objects.  Una are the objects that are both Selfish 
(have a creation identity) and are PassByConstruction.  A reference to an 
Unum always resolves either to a broken reference or to a reference to a 
Near presence of the Unum; you cannot have a resolved far reference to an Unum.


>It may be reasonable to build a widget kit that knows it is
>communicating eventually, when it asks the model for display
>information.  The model events could be SendOnly callbacks to the view,
>as well.   This makes me wonder how E implements an event system.  I'm
>actually a bit rusty on how Java manages this as well.  :)   In Squeak,
>we use either a #changed:/#update: broadcast system or a
>#when:send:to:/#triggerEvent: multicast system (and there is a morphic
>varient, for mouse and keyboard events, with the protocol #on:send:to:).

Again, I don't know Morphic.  Classic Smalltalk-80 dependents did not use an 
event system, instead calling all the dependents immediately during the 
mutate action they were being informed of, which can cause all sorts of 
horrible bugs.  Instead, as explained at 
http://www.erights.org/elib/concurrency/turns.html , each dependent should 
be notified in its own separate event (or "turn", as we call it).  

Regarding how E implements events, the FERunner uses the existing AWT Event 
Queue and Thread, so that objects running in an FE (front-end) vat may have 
synchronous access to and from gui objects.  The BERunner uses its own queue 
and Thread, so that back-end (headless) computations can run concurrently 
with everything else.  For further information, see the files 
e/src/jsrc/org/erights/e/elib/prim/*Runner.java


-----------------------------------------------------------
The optimistic queue example from 
e/src/esrc/org/erights/e/examples/concurrency/QMaker.emaker :
-----------------------------------------------------------
# Copyright 2002 Combex, Inc. under the terms of the MIT X license
# found at http://www.opensource.org/licenses/mit-license.html

def QMaker() :near {
    def [var qhead, var qtail] := makePromise()
    def cons(elem, next) :any {
        def pair {
            to getElem() :any { elem }
            to getNext() :any { next }
        }
    }
    [def enqueue(elem) :void {
        def [nextHead, nextTail] := makePromise()
        qtail resolve(cons(elem, nextHead))
        qtail := nextTail
    },
    def dequeue() :any {
        def result := qhead <- getElem()
        qhead := qhead <- getNext()
        result
    }]
}

    ? def QMaker := <import:org.erights.e.examples.concurrency.QMaker>
    # value: <QMaker>
    
    ? def [enq, deq] := QMaker()
    # value: [<enqueue>, <dequeue>]
    
    ? enq(3)
    ? def x := deq()
    # value: <Eventual ref>

Wait a moment

    ? x
    # value: 3
    
    ? def y := deq()
    # value: <Eventual ref>

Wait a moment

    ? y
    # value: <Eventual ref>

Yup, still unresolved

Messages to promises build up future dependency chains in a natural way:

    ? def z := y <- add(7)
    # value: <Eventual ref>

    ? enq(4)

Wait a moment

    ? y
    # value: 4
    
    ? z
    # value: 11


----------------------------------------
Text by me above is hereby placed in the public domain

        Cheers,
        --MarkM