[e-lang] Orc in E with a first working test case
Mark S. Miller
markm at cs.jhu.edu
Fri Oct 27 22:15:54 CDT 2006
On Wednesday I heard a wonderful talk by Pref. Jay Misra on his new Orc
language.
http://www.cs.utexas.edu/users/wcook/papers/OrcJSSM05/OrcJSSM.pdf
http://www.cs.utexas.edu/users/wcook/projects/orc/
http://www.stanford.edu/class/ee380/Abstracts/061025-slides.pdf
I invited him to give the talk again at HP Labs, which he did. Many of the
local capabilities crowd attended, and we had a lively discussion.
Orc has many of the same goals as some of our systems (Joule, E, Joe-E +
Waterken, etc...). Its emphasis is on the distributed concurrency control side
of things. I was simply amazed at its combination of expressiveness and
simplicity. Although Orc resembles other process algebra systems, as far as I
could tell, it does not make unreasonable assumptions about what's
implementable. It also looks to me like a much easier system to teach, learn,
and use.
Below is a draft implementation of Orc as I currently understand it in E,
followed by a first working test case based on Dr. Misra's first example.
------------------------------------------------------------------------------
# Copyright 2006 Hewlett-Packard, Inc. under the terms of the MIT X license
# found at http://www.opensource.org/licenses/mit-license.html ...............
pragma.syntax("experimental")
def promiseAllResolved := <elang:interp.promiseAllResolved>
def [never,_] := Ref.promise()
def orc {
########################### orc syntactic primitives ####################
/**
* orc`@site(@args*)` => e`orc.call($site, [$args*])
* <p>
* We assume that a nullary call to a site is written as
* orc`@site()`, so that we may interpret orc`@site` as
* an expression whose value is the channel to that site,
* rather than a call on that channel.
*/
to call(site, args) {
def callAgent(term, chan) {
if (!Ref.isResolved(term)) {
when (promiseAllResolved(args)) -> {
def result := E.send(site, "run", args)
when (result) -> {
chan <- publish(result)
}
}
}
}
return callAgent
}
/** orc`@left | @right` => e`orc.par($left, $right)` */
to par(leftAgent, rightAgent) {
def parAgent(term, chan) {
if (!Ref.isResolved(term)) {
leftAgent <- run(term, chan)
rightAgent <- run(term, chan)
}
}
return parAgent
}
/**
* orc`@left >@x> @right` => e`orc.pipe($left, fn $x{$right})`
* orc`@left >> @right` => e`orc.pipe($left, fn _{$right})`
*/
to pipe(leftAgent, rightAgentFn) {
def pipeAgent(term, chan) {
def rightChan {
to publish(val) {
if (!Ref.isResolved(term)) {
rightAgentFn <- run(val) <- run(term, chan)
}
}
}
leftAgent <- run(term, rightChan)
}
return pipeAgent
}
/**
* orc`@left where @x:elem-of @right` =>
* e`orc.where(fn $x{$left}, $right)`
*/
to where(leftAgentFn, rightAgent) {
def whereAgent(term, chan) {
def [x,xR] := Ref.promise()
leftAgentFn <- run(x) <- run(term, chan)
def [rightTerm,rightTermR] := Ref.promise()
def leftChan {
to publish(val) {
if (!Ref.isResolved(term)) {
xR.resolveRace(val)
rightTermR.resolveRace(null)
}
}
}
when (term) -> { rightTermR.resolveRace(null) }
rightAgent <- run(rightTerm, leftChan)
}
return whereAgent
}
################## primitive Orc command line interpreter ##############
/**
* To run an orc expression as a command, do
* <pre> orc.go(currentVat, agentExpr)</pre>
* where agentExpr is the translation of the orc syntactic primitives
* into E agent creation calls listed above.
* This returns a promise for the results published by this agent by the
* time the local vat quiesces.
*/
to go(currentVat, agent) {
var results := []
def goChan {
to publish(val) {
results with= val
}
}
# XXX A more realistic command line interpreter would provide the
# caller a way to resolve the term argument, in order to stop the
# command.
agent(never, goChan)
def getResults() {
if (currentVat.isQuiescent()) {
return results
} else {
return getResults <- run()
}
}
return getResults <- run()
}
####################### built in sites ########################
/** orc`0` (bold, not number) => e`orc.zero` */
to zero() { return never }
/** orc`if` => e`orc.test` */
to test(cond) { return if (cond) { null } else { never } }
/** orc`Signal` => e`orc.signal` */
to signal() { return orc.test(true) }
/**
* orc`Rtimer` => e`orc.makeRtimer(timer)`
* <p>
* Note that the E expansion references "timer", which is bound in the
* privilegedScope but not in the safeScope. The binding for timer must be
* provided by other means, as is necessary, since it represents a source
* of authority.
*/
to makeRtimer(timer) {
def rtimer(t) {
return timer.whenPast(timer.now() + t,
fn{orc.signal()})
}
return rtimer
}
/**
* orc`let` => e`orc`
* <p>
* In order to simulate the ML-like tuple, which Orc demands, this method
* will pick up the 1-arity case, yielding just the value, while the
* match clause will pick up the remaining cases, returning a list of
* values.
*/
to run(val) { return val }
match [`run`, args] { args }
}
# rune(["~/e/src/esrc/scripts/updoc.e", "~/e/src/esrc/com/hp/orc/orc.emaker"])
? def orc := <import:com.hp.orc.orc>
? pragma.syntax("experimental")
? def CNN() { return "US News" }
# value: <CNN>
? def BBC() { return "UK News" }
# value: <BBC>
? def newsAgent := orc.par(orc.call(CNN, []), orc.call(BBC, []))
# value: <parAgent>
? def printNewsAgent := orc.pipe(newsAgent, fn m { orc.call(println, [m]) })
# value: <pipeAgent>
? def results := orc.go(currentVat, printNewsAgent)
? interp.waitAtTop(results)
? results
# stdout: US News
# UK News
#
# value: [null, null]
More information about the e-lang
mailing list