[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