The Declaration Approach (was: Some thoughts on the 'reveal' operator)

Chip Morningstar chip@communities.com
Wed, 20 Oct 1999 12:22:20 -0700 (PDT)


>In the declaration approach, the variable name in the header is a defining 
>occurrence of this variable.  Let's say, for example, that x is a char and y 
>is an integer:
>
>     define f(x) => y { y := x asInteger }
>
>Well, this works, but it seems weird to need assignment for what should be a 
>side effect free program.

It just seems weird to have the declaration and the assignment right next to
each other like that. It encourages you to think that a better form might be:

     define f(x) => x asInteger

which I think is a full circle return to the very problem we're trying
to solve, only without the braces, i.e., this is not substantively different
from:

     define f(x) { x asInteger }

The problem here is basically that this is fine syntax for this particular
example. In other words, this is not the problematic case. Where we get in
trouble is when we have blocks containing multiple expressions and when we have
nested blocks.  The problem really stems, in my opinion, from the
counter-intuitiveness of combining Lisp-ish "the value of a sequence of
expressions is the value of the last expression in the sequence" semantics with
the C-ish statement-like block syntax that discourages you from thinking of a
block as a sequence of expressions that has a value.

Your earlier approach of introducing an explicit reveal operator was on the
right track, I think. It's just that when applied with naive ruthless
consistency it yielded absurd and surprising constructs in some cases.

Perhaps it would be good to first restate what the problem is that we're trying
to solve here, because you seem to be following a chain of local problems each
of which stems from attempts to patch the previous problem. Time to go back to
the beginning.

My understanding of the original problem is: it's too easy to accidently let
values escape from blocks that you didn't really intend them to escape from.

If this is the problem, then here's my take:

Blocks appear in two different kinds of syntactic contexts, which I'll loosely
label "lambda-like" (e.g., define) and "group-like" (e.g., if). In the first
case the block's primary purpose is to provide a lexical scope (i.e., to
determine what symbols are visible where). In the second case the block's
primary purpose is to provide an execution scope (i.e., to determine what
statements an execution modifier such as "if" refers to). These cases are
confused because in each case the block also does the other thing as well (and
actually we want it to do that, so we don't really want to treat these as truly
separate constructs, e.g., with different block delimiters or something).

A value escaping from a group-like context is not a problem because a
group-like context is always syntactically embedded (ultimately) inside a
lambda-like context. What we want to control is the escape of values from the
latter. So if we want to do some syntactic engineering here, the first thing to
note is that we need only apply our efforts to the lambda-like contexts and not
to the group-like contexts (it may be helpful to look examine the overall
language syntax with an eye to which is which). The first-cut reveal syntax was
goofy because it also tried to control value escape from group-like contexts,
which just introduced pointless busywork for the programmer.

The next question that arises in my mind is whether the directive to reveal a
value is applied (syntactically) to the block or to the value itself. My
intuition says to apply it to the block, which would put the kabosh on the
declaration approach you outlined today. I favor applying it to the block
because (1) my gut says this will be syntactically less cumbersome, and (2)
applying to the value may lead to programmers thinking in C-ish "return"
statement terms, which would lead in turn to a whole 'nother set of confusions.