ERights Home elang / blocks 
Back to: Inheritance as aStatic Pattern of Message Passing No Next Sibling

Miranda Methods


When we first created lazyNum, where did the "<lazy>" string that was printed out come from? After the E command interpreter reads and evaluates an expression, it then needs to print a string to represent the resulting value. It gets this by calling the result's __printOn method

? pragma.syntax("0.8")

? def makePoint(x, y) :any {
>     def point {
>         to __printOn(out) :void { out.print(`<$x, $y>`) }
>         to getX() :any {x}
>         to getY() :any {y}
>     }
> }
# value: <makePoint>

? def pt := makePoint(3, 5)
# value: <3, 5>

? pt.__printOn(stdout)
# stdout: <3, 5>

? makePoint.__printOn(stdout)
# stdout: <makePoint>

Above, the object expression for point explicitly provides a __printOn method, and this is what gets run when __printOn is called on a point. The object expression for makePoint, on the other hand, does not provide a __printOn method, but nevertheless, we see that it has a __printOn behavior. __printOn is one of a small set of Miranda Methods -- if the object-expression does not supply its own __printOn method, one will be provided for it. Similarly for the other Miranda Methods.

Rarely, in order to have full control, you may wish to define an object while waiving all your miranda rights, rather than just overriding individual methods. You do this by using a plumbing expression instead of an object expression.

The Miranda Methods can also be used from ELib, as documented here. (There is a distressing amount of redundancy between that page and this one. We need to fix this, probably when we build edoc.)

The Miranda Methods are:

The Miranda Methods are actually implemented in Java and operate from within the E implementation. A few rely on that special position to implement behaviors that could not otherwise have been implemented in E. In this section, we will do our best job of using E anyway as a notation to explain what these methods do, but explain in the text what the differences are.

  • __printOn(out)

    Asks the object to print a representation of itself onto the TextWriter, using "out.print()" and its siblings. The object should print subobjects by sending them to the same TextWriter (or any indentation of it) rather than coercing these to a string itself, in order to give the TextWriter the opportunity to finitely print cyclic structures.

    The Miranda behavior is

    to __printOn(out :TextWriter) {
        out.print("<", /*name of object expression*/, ">")
    }

    except that an E object has no other way to ascertain the name of its defining object expression. If the defining object expression is anonymous, then the object prints as

    <e object>
  • __whenMoreResolved(observer)

    Really used to test a reference rather than an object. When sent to a Promise, should the Promise ever become Resolved, the resolution is eventually sent to observer. If it is sent to a Far reference, even though a Far reference is already Resolved, the __whenMoreResolved message will still be sent to the object the Far reference designates, so that it can respond. This ensures that a __whenMoreResolved does not report successful resolution (notifying the observer with a non-Broken argument reference) until all earlier messages sent on this same reference have been received.

    The when/catch construct expands into a call to "Ref.whenResolved(...)", which sends __whenMoreResolved.

    The Miranda behavior is

    to __whenMoreResolved(observer) {
        observer <- run(self)
    }
  • __whenBroken(observer)
    Used to ask a reference to notify the observer should it ever become broken. By virtue of the behavior below, a Near reference ignores the request, since it cannot become Broken. A Broken reference immediately responds with itself. Eventual references, whether Resolved or not (whether a Promise or a Far reference) remember the request so they can notify the observer should they become Broken. In all cases, of the observer is invoked, it is eventually send the Broken reference as the argument of a run message.

    The Miranda behavior is

    to __whenBroken(observer) {}
  • __order(nestedVerb, nestedArgs) => [result, self]
    Without the order message, E already provides for the partially-ordered fail-stop delivery of messages sent on the same reference. However, this notion of fail-stop only makes later delivery contingent on the reference remaining unbroken. You can use the order message to get the same effect, but have later deliveries contingent on the synchronous success of an earlier message as well.

    The Miranda behavior is equivalent to:

    to __order(nestedVerb, nestedArgs) :any {
        [E.call(self, nestedVerb, nestedArg), self]
    }

    If the call throws, then the order message as a whole throws. A promise for the result of an order message becomes broken, rather than resolving to a pair of a broken promise and self.

    ? def x := 6 <- __order("floorDivide", [2]) <- get(1) <- add(2)
    # value: <Promise>
    
    ? x
    # value: 8

    8 is indeed 6 + 2.

    ? def y := 6 <- __order("floorDivide", [0]) <- get(1) <- add(2)
    # value: <Promise>
    
    ? y
    # value: <ref broken by problem: <ArithmeticException: / by zero>>
    We never asked 6 what the "+ 2" of itself is, because the earlier floorDivide threw an ArithmeticException. Therefore, the promise for the result of the __order/2 message became broken by this exception, as did the promise for the get/1 message sent to this promise, and the promise for the add/1 message sent to that promise. This contingency of the latter operation happened without waiting on a round trip to find out about the earlier operation, and so still made full use of pipelining.
  • __getAllegedType()
    getAllegedType, which will return a Type object describing the protocol that this object alleges it responds to.

    The Miranda behavior is

    to __getAllegedType() :Type {
        /*not expressible in E*/
    }
  • __respondsTo(verb, arity)
    Does this object respond to messages with this verb and this arity?

    The Miranda behavior is equivalent to

    to __respondsTo(verb :String, arity :int) :boolean {
        self.__getAllegedType().hasMethod(verb, arity)
    }
  • __yourself()
    Used internally by CapTP to ensure that message are delivered in the proper order when Alice, Bob, and Carol are on three separate machines. Is available for use by the E programmer, but it's hard to see when it would be useful. It you find a programming technique that makes use of yourself, please let me, the webmaster-at-erights.org, know.

    The Miranda behavior is

    to __yourself() :any {
        self
    }
  • __reactToLostClient(problem)

    Informs the object that at least one of its clients may no longer be able to communicate to it, due to a network partition.

    To be written. See Dead-Man Switch.

  • __optSealedDispatch(brand)

    Generic object-level rights amplification protocol.

    To be written.

 
Unless stated otherwise, all text on this page which is either unattributed or by Mark S. Miller is hereby placed in the public domain.
ERights Home elang / blocks 
Back to: Inheritance as aStatic Pattern of Message Passing No Next Sibling
Download    FAQ    API    Mail Archive    Donate

report bug (including invalid html)

Golden Key Campaign Blue Ribbon Campaign