Here are the highlights of 0.8.1:
We now have a binary as well as a source distribution of E
Tyler is maintaining the new E download page https://www.cypherpunks.to/erights/download.html and has built full crypto versions of all the variants of the daffE distribution. Both daffE and E are now to be downloaded only from Tonga. There remains no convenience advantage for using daffE. Thanks Tyler!
universalScope & privilegedScope
The starting scope, that all *.e programs and elmer sessions were starting with, is now known to itself as the "privilegedScope". Even from a descendent scope, if this name isn't shadowed, you can access this scope by the name "privilegedScope". This scope confers *all* your authority (eg, "file:"), so should only be handed to code you fully trust. Capability style says that this should only be "big bang" code, whose only purpose is to divvy up these authorities, handing out "least" authorities to the objects that will do the work.
The privilegedScope descends from the now separate universalScope, known to itself (and there to the privilegedScope) as "universalScope". It should not grant any authority, and except for "import:" it does not. E programs import Java classes and ".emaker"s (see below) using "import:". Until the Java-E binding is restricted (likely by manual inspection), and emakers are restricted (by auditors, see below) so that neither can provide static authority, we don't yet have a local capability system. (But remember that we do already have a distributed capability system, and that's vastly more important.) In any case, these restrictions will be turned on, so we should program now as if they were already enforced.
Static Scope Error Detection
If a top-level E expression uses a variable name not defined in its scope, the attempt to evaluate this expression results in an immediate exception. Thanks MarcS!
Modules: *.emaker Files
The big deal in this release is the ability to break an E program into separately loaded files. These live on the java CLASSPATH as *.emaker files, so an "import:"er doesn't care if he's importing Java, E, or E compiled to Java. *.emaker files are evaluated in the universalScope.
Using this mechanism, we now have several such *.emaker"s:
ListMaker (used by the expansion of [...]) PerlMatcherMakerMaker (the rx`...` quasi-parser) MintMaker (traceable, but otherwise secure money) TitleCo (exclusive rights transfer) QMaker (strange concurrency example)
Btw, I should mention that the MintMaker derives from the KeyKOS meter, the TitleCo derives from the KeyKOS space bank, and the QMaker derives from a Flat Concurrent Prolog example. Thanks, Norm & Udi. Thanks also to Ping for clarifying some Perl regular expression syntax issues.
E Modules Provide Their Own Executable Examples
The E Lexer now considers all lines in which the first non-whitespace character is a "?" or a ">" to be a comment. Of course, Elmer/Updoc ignores all lines other than these. This gives a file two readings: E and Updoc. When a file contains both, it is expected that the updoc contents are illustrative examples of the functions of this very module. When E supports /*...*/ comments, then updoc lines can illustrate comments in the way they're meant to. The E code is evaluated in the universalScope. By default, the updoc code is evaluated in the privilegedScope.
Robust Local Cycle Handling
Virtuous Cycles:
The java printing style of recursive composition of toString() messages can't finitely print cyclic structures without resorting to hidden global state (and thereby breaking capability discipline.) Instead, E has now switched to a Smalltalk-like printOn(oo) discipline, where oo is a TextWriter. The printOn(oo) method then prints it's pieces with "oo print(...)" (which can take several arguments). The TextWriter keeps track of what objects it's in the middle of printing, and prints "***CYCLE***" if asked to print one again:
? define a := [1, [2, a], 4]
# value: [1, [2, ***CYCLE***], 4]
Equality is well defined on selfless virtuous cycles but not yet implemented. Two selfless (see below) virtuous cycles are the same if they represent the same infinite structure. It doesn't matter if one is wound tighter than the other. Since it's not yet implemented, the following is hypothetical:
? define b := [1, [2, [1, [2, b], 4], 4], 4]
# value: [1, [2, ***CYCLE***], 4]
? a == b
# value: true
Vicious Cycles:
Something Henry Baker (thanks!) raised wrt Joule that we never did figure out: what about a Channel (in E, a Promise) that's forwarded to itself? The data cycle above, though strange, represents a perfectly well formed infinite structure, with a clear meaning -- a virtuous cycle. Similarly, the cycle shown in http://www.erights.org/elang/concurrency/epimenides.html has a clear and implementable (implemented!) meaning. But what would DEFERRED references, each pointing to the next around a loop, mean? E's new answer is that all references participating in such a loop get broken by a ViciousCycleException. The cycle detection turned out to be almost free.
? define a := a
# value: <Ref broken by org.erights.e.elib.prim.ViciousCycleException: # Promise loop>
? define [p1,r1] := PromiseMaker()
# value: [<Deferred ref>, Unresolved Resolver]
? define [p2,r2] := PromiseMaker()
# value: [<Deferred ref>, Unresolved Resolver]
? define x1 := p1 <- add(3)
# value: <Deferred ref>
? r1 resolve(p2)
? r2 resolve(p1)
? p1
# value: <Ref broken by org.erights.e.elib.prim.ViciousCycleException: # Promise loop>
? p2
# value: <Ref broken by org.erights.e.elib.prim.ViciousCycleException: # Promise loop>
? x1
# value: <Ref broken by org.erights.e.elib.prim.ViciousCycleException: # Promise loop>
By the normal broken promise contagion rules, all references pointing into such a loop, and the optimistic outcomes of messages set into such a loop (like x1 above) also become broken by a ViciousCycleException.
We currently only have this working locally (for RevokableRefs as well as Promises). A cross machine forwarding loop is not currently detected, and messages introduced into it will circulate forever. I think I know how to fix this, also essentially for free.
As part of this work, I also turned Sendable from an interface to a class so I could use implementation inheritance (!) to factor out code common to various Sendable implementations.
The attitude
Don't prevent cycles, handle them. It's no more work and it's much more rewarding.
comes from Prolog III. Thanks, Colmerauer (sp?). The algorithmic insight that enables me to detect LOCAL forwarding cycles essentially for free (and without any hash tables) is Eric Drexler's. Thanks.
Audited Object Expressions
I've extended the syntax in expectation of allowing audited object expressions: after the naming part, you may optionally provide a "::" followed by a list of identifiers separated by commas. See "Selfish and Selfless Objects" in http://www.erights.org/elang/same-ref.html for the rationale of auditing that an object is "selfless". The plausibly auditable properties I can currently think of are
immutable fullyImmutable transparent fullyTransparent selfless fullySelfless fullyConfined fullyDeterministic
The prefix "fully" really means "transitively". There are no non-fully "confined" or "deterministic" because these properties only make sense transitively. By "fullyConfined", I mean approximately what KeyKOS/EROS means by "sensory".
The object that a .emaker file evaluates to will have to be fullyImmutable. All throwables have to be fullySelfless. fullyImmutable implies fullyConfined. fullyDeterministic implies fullyImmutable, but (I'm still trying to understand this one) may be prohibited from using "<-" (since there's non-determinism there), and may be prohibited from using
try {
...
} finally {
...
}
Since that can change state in response to Java VirtualMachineErrors, an unavoidable source of non-determinism. (Thanks Dan!) A memoizing cache around a fullyDeterministic object is itself fullyDeterministic, and therefore fullyImmutable, even though it contains shared mutable state, since no one can tell. A fullyDeterministic object cannot read a covert channel without it's confiner's cooperation.
Initially, I expect to be building particular auditors in as part of the TCB, but I'm defining a semantics so, once we have more experience, we can let them be provided without privilege. An auditor is given the object expression parse tree, not to transform like a macro would, but to either approve or not. (That way I can have several auditors and not worry about finding an approval fixedpoint.) An auditor would then be able to verify for others (using Joule verifier protocol) whether a given LOCAL object is an instance of an object expression audited by that auditor. In other words, is this object's behavior guaranteed to have the property the auditor represents.
I've also changed the syntax of the innocent looking "scope" and turned it into "meta scope". "scope" is no longer a keyword, but "meta" is. "meta
behavior(<name>)", where <name> is the definition name of an enclosing object expression, will return the object expression parse tree of that object expression, but this isn't yet implemented. Other funny operationscan be added as other extensions of meta, so the one keyword "meta" alerts you that something funny is going on.
The "revealInnards" message explained on http://www.erights.org/elang/same-ref.html uses both current forms of meta in order to auditably achieve transparency.