[E-Lang] Rights Amplification: The Next Layer Up
Mark S. Miller
markm@caplet.com
Tue, 05 Sep 2000 20:37:20 -0700
I wrote:
> to getOptSealedFacet(brand :Brand) :(nullOk(SealedBox)) {
> switch (brand) {
> match ==(FooSealer brand) {
> FooSealer seal(...my facet for Foo...)
> }
> match ==(BarSealer brand) {
> BarSealer seal(...my facet for Bar...)
> }
> match _ {
> meta getDefaultOptSealedFacet(self, brand)
> }
> }
> }
However, only two lines of these 13 say anything significant. The rest is
business as usual. OTOH, we don't know how often we'll need to say any of
this, since the default policies should almost always be adequate. If we
rarely need to say any of this, it doesn't justify the need for syntactic
sugar, since the benefits of sugar are proportional to usage, but the costs
(in language understandability) remain even when the sugar is unused. And
occasionally used sugar has a huge cost on those reading such code.
Therefore, the following proposal is not something I would make part of the
language until we have more experience with the manual form. (However, like
other possible future syntax features, I have added the productions to the
parser, to ensure that we can add them later if choose.) I like to have
such things in my back pocket in case we need them.
The key observation (from Joule's "Energetic Secrets"
ftp://ftp.webcom.com/pub1/agorics/postscript/MANUAL.B17.ps.Z ) is 1) as
mentioned earlier, a brand identity can be treated like a message name,
identifying the semantics of a request, and 2) therefore dispatching on
brand-match is like dispatching on message name. Seen this way, our
standard objects dispatch on both message names (actually message name +
arity) and on brands. The syntactic sugar can suggest this view:
define objName {
to msgName1(params1...) :retType1 { methodExpr1 }
to msgName2(params2...) :retType2 { methodExpr2 }
...
facet (sealerExpr1) => (facetExpr1)
facet (sealerExpr2) => (facetExpr2)
...
match pattern1 { matcherExpr1 }
match pattern2 { matcherExpr2 }
...
}
Kernel-E would still only accept multiple methods followed by zero or one
match clauses. Just as multiple match clauses in User-E are gathered
together into a single Kernel-E match clause
match x {
if (x =~ pattern1) {
matcherExpr1
} else if (x =~ pattern2) {
matcherExpr2
} else {
throw(...)
}
multiple facet clauses in User-E would be gathered together into a single getOptSealedFacet/1 method.
to getOptSealedFacet(brand :Brand) :(nullOk(SealedBox)) {
switch (brand) {
match ==((define s1 := sealerExpr1) brand) {
s1 seal(facetExpr1)
}
match ==((define s2 := sealerExpr2) brand) {
s2 seal(facetExpr1)
}
match _ {
meta getDefaultOptSealedFacet(self, brand)
}
}
}
Because side effects should be avoided in the getOptSealedFacet/1 method,
the sealerExprs and facetExprs will typically just be variable names.
However, even when they're not, the above code avoids evaluating anything
more than once, and it evaluates everything in the order and scope expected
from reading the source.
It would be a syntax error for an object is written with both facet clauses
and an explicit getOptSealedFacet/1 method. Such programs would be
statically rejected.
The caller of getOptSealedFacet/1 has a corresponding issue in attempting to
amplify:
I wrote:
> to replaceObject(original) :any {
> def optBox := original getOptSealedFacet(myUnsealer brand)
> if (optBox == null) {
> original
> } else {
> myUnsealer unseal(optBox)
> }
We could add a new pattern production that parallels the above facet clause:
facet (unsealerExpr) => pattern
Which evaluates the unsealerExpr, asks the specimen for a sealed box of that
brand, checks that its brand matches the brand of the presumed box, unseals,
and matches the contents of the box with the sub-pattern on the right. This
sugared pattern would expand to:
x ? (def u1 := unsealerExpr;
x getOptSealedFacet(u1 brand) =~ box ? (box != null) &&
u1 unseal(box) =~ pattern)
Using this, the previous method could be written
to replaceObject(original) :any {
if (original =~ facet(myUnsealer) => result) {
result
} else {
original
}
}
or equivalently
to replaceObject(original) :any {
switch (original) {
match facet(myUnsealer) => result { result }
match _ { original }
}
}
Not enough bang for the buck to bother with, but I thought I'd let y'all
know before I forget it. I think there may be better ideas lurking nearby.
Cheers,
--MarkM