Miranda order
From Erights
__order/2
__order/2 is a Miranda message for sending messages contingent on an earlier success.
This method does the equivalent of
to __order(verb, args) :any { [E.call(self, verb, args), self] }
In other words, it calls the receiving object with the message
verb(args...)
, and, if successful, returns a pair
of the result of this call and the receiving object itself.
What is this for? Consider the client code fragment
databaseRcvr <- put(key1, value1) def value2Vow := databaseRcvr <- get(key2)
E's partial ordering semantics ensure that put will be delivered before get is delivered. That is often good enough. But it is a weaker guarantee than that provided by the following sequential code
database put(key1, value1) def value2Vow := database get(key2)
In this code, not only will get only happen after put is delivered, get will only happen after put succeeds. If put instead throws an exception, the get will never happen. Often we want this effect. How can we achieve this with eventual-sends to eventual references?
When one wants to take an action contingent on the results of a previous action, the conventional E answer is to use a when-catch-finally expression
def ackVow := databaseRcvr <- put(key1, value1) def value2Vow := when (ackVow) -> { databaseRcvr <- get(key2) } catch problem { throw(problem) }
This is fine, as is probably the solution to be used by default for this situation. However, it does force a round-trip between the get and put, and so loses the benefits of pipelining. Using the __order message, we can get contingent execution + pipelining, at some cost in obscurity. (Note: often, the cost of obscurity will dominate.)
def pairVow := databaseRcvr <- __order("put", [key1, value1]) # If put's return value were interesting, we'd 'pairVow <- get(0)' def newDBRcvr := pairVow <- get(1) def value2Vow := newDBRcvr <- get(key2)
If put throws, then pairVow will resolve directly to broken, so newDB will likewise resolve to broken, as will value2Vow.
Distinguishing unresolved response from unresolved value
It is also possible to use __order to observe when the turn caused by an eventual send finishes, even if its return value is itself an unresolved promise, by waiting for the returned tuple, or its second element, to resolve.