Some thoughts on the 'reveal' operator

Dean Tribble tribble@netcom.com
Tue, 28 Sep 1999 14:10:49 -0700


--=====================_1206759228==_.ALT
Content-Type: text/plain; charset="us-ascii"; format=flowed

I am currently entertaining delusions of writing a long message in response 
to the persistence thread and on reveal.  For the moment I will settle with 
the fast response to reveal

>Of course, this should not at all inhibit the rest of you from arguing 
>about it until I can pay attention again.  Dean, you said (verbally to me) 
>that E should handle this the way Joule did.  Would you care to explain, 
>and to say what that would mean for E?

One of the syntactic niceties in E is the "expression"-language style 
syntax.  The root of the "reveal" problem is that "expression" syntax is 
nice in large part because it makes returning results the default behavior, 
and you don't want people accidentally returning results they didn't intend.

There were several reasons we didn't do an expression syntax in Joule, but 
one of the major ones was precisely the issue of revealing results.  Note 
that we decided to use the term "reveal" for Joule precisely because values 
are never "returned".  Instead, a Distributor is forwarded to the port for 
the result, thus revealing the answer.  The consequence of this is that 
reveal is an explicit operation.  Thus, Chip's example (in the unfinished 
Joule syntax revision that will look eerily familiar):

def factorial(n, &res) {
   if (n <= 0) {
     res -> 1
   } else {
     res -> n * factorial(n-1)
   }
}

Note that a message send in an expression context implicitly inserts a 
channel (just as with the published syntax).  Thus, things are only 
revealed explicitly.  With expression languages, you have a few other 
alternatives:

def factorial(n, &res) {
   res -> if (n <= 0) { 1 }
          else { n * factorial(n-1) }
}

def factorial(n) {
   reveal if (n <= 0) { 1 }
          else { n * factorial(n-1) }
}

function factorial(n) {
   if (n <= 0) { 1 }
   else { n * factorial(n-1) }
}

etc.  The simplest way to address it in E is to require an explicit 
difference for procedures that return results, rather than requiring 
fine-grained labelling of statements within a procedure fir which values 
might be returned.  BTW type systems can actually help a lot with this 
problem, because often the errors that expose too much also expose objects 
with a different type than the intended one (especially if your 
abstractions are designed that way).

>As long as we're talking syntax design, I'll go ahead and revive an old 
>question: Ping especially has long advocated that E adopt Python's use of 
>indentation rather than curly brackets for block structure.  My 
>counter-argument has been we tried that in Joule, but got killed by tab 
>ambiguity.  Most everyone agrees

Because of the type problem, in Joule we moved to a Pascal-style 
keyword-based syntax so that it didn't look like punctuation, but could 
still be typed on a single line (i.e., it was unambiguous).  I still like that:

def factorial(n, &res)
   if (n <= 0)
     res -> 1
   else
     res -> n * factorial(n-1)
   endif
end

but familiarity pushes towards use of braces.

>Alternatively, how about if tab was simply not considered as valid 
>character to be present in E source code?  A conforming lexer would be 
>required to reject such input.  Therefore, if one's editor outputs tab 
>characters, one would have to convert these programs before 
>submission.  We can trivially supply such conversion programs.  I'm sure 
>we examined and rejected such a solution in the Joule days, but I can't 
>remember why.

I do not believe we ever considered this option.

--=====================_1206759228==_.ALT
Content-Type: text/html; charset="us-ascii"

I am currently entertaining delusions of writing a long message in
response to the persistence thread and on reveal.  For the moment I
will settle with the fast response to reveal

Of course, this should not at all inhibit the rest of you from arguing about it until I can pay attention again.  Dean, you said (verbally to me) that E should handle this the way Joule did.  Would you care to explain, and to say what that would mean for E?

One of the syntactic niceties in E is the "expression"-language style syntax.  The root of the "reveal" problem is that "expression" syntax is nice in large part because it makes returning results the default behavior, and you don't want people accidentally returning results they didn't intend.

There were several reasons we didn't do an expression syntax in Joule, but one of the major ones was precisely the issue of revealing results.  Note that we decided to use the term "reveal" for Joule precisely because values are never "returned".  Instead, a Distributor is forwarded to the port for the result, thus revealing the answer.  The consequence of this is that reveal is an explicit operation.  Thus, Chip's example (in the unfinished Joule syntax revision that will look eerily familiar):

def factorial(n, &res) {
  if (n <= 0) {
    res -> 1
  } else {
    res -> n * factorial(n-1)
  }
}

Note that a message send in an expression context implicitly inserts a channel (just as with the published syntax).  Thus, things are only revealed explicitly.  With expression languages, you have a few other alternatives:

def factorial(n, &res) {
  res -> if (n <= 0) { 1 }
         else { n * factorial(n-1) }
}

def factorial(n) {
  reveal if (n <= 0) { 1 }
         else { n * factorial(n-1) }
}

function factorial(n) {
  if (n <= 0) { 1 }
  else { n * factorial(n-1) }
}

etc.  The simplest way to address it in E is to require an explicit difference for procedures that return results, rather than requiring fine-grained labelling of statements within a procedure fir which values might be returned.  BTW type systems can actually help a lot with this problem, because often the errors that expose too much also expose objects with a different type than the intended one (especially if your abstractions are designed that way).

As long as we're talking syntax design, I'll go ahead and revive an old question: Ping especially has long advocated that E adopt Python's use of indentation rather than curly brackets for block structure.  My counter-argument has been we tried that in Joule, but got killed by tab ambiguity.  Most everyone agrees

Because of the type problem, in Joule we moved to a Pascal-style keyword-based syntax so that it didn't look like punctuation, but could still be typed on a single line (i.e., it was unambiguous).  I still like that:

def factorial(n, &res)
  if (n <= 0)
    res -> 1
  else
    res -> n * factorial(n-1)
  endif
end

but familiarity pushes towards use of braces.

Alternatively, how about if tab was simply not considered as valid character to be present in E source code?  A conforming lexer would be required to reject such input.  Therefore, if one's editor outputs tab characters, one would have to convert these programs before submission.  We can trivially supply such conversion programs.  I'm sure we examined and rejected such a solution in the Joule days, but I can't remember why.

I do not believe we ever considered this option.
--=====================_1206759228==_.ALT--