The Declaration Approach (was: Some thoughts on the 'reveal' Mark S. Miller (markm@caplet.com)
Wed, 20 Oct 1999 10:57:36 -0700

First of all, my apologies to the list for the spottiness and intermittentness (is this a word?) of my participation in the various discussions. I am in the midst of attempting to pull together a startup company, Combex.com, that would apply E and related technologies to a potentially lucrative area of business. Although I've been involved in founding startups before, I've always "just been a technical guy". I had no idea how distracting it can be to be the point guy. I fear my attention to the list will continue to be spotty for the near future, but I'll try to catch up on things here and there.

First, I'll focus on those issues I'd like to resolve by the 0.8.5 release. One of these is the "reveal" issue, since this would be a non-upward compatible syntactic change. By most measures, it should already be considered too late for such changes, but this one's important enough to happen anyway, as y'all seem to agree.

I like the MarcS/Doug/Mesa approach. However, there are three variants I see: 1) the name in the function/method declaration header declares the named variable, 2) the name in the header is a use of the named variable, and 3) we merely mark in the header whether we are revealing the body's value or not. (#3 is not really a variant of the MarcS/Doug/Mesa approach, but I got there by considering variants of #2.)

#1 is more intuitive and more pleasant in several ways, but I cannot
make it work out to my satisfaction. I have a workable proposal for #2, but its explanation is unfortunately complex and counter-intuitive.
#3 doesn't have some of the nice readability properties of #1 or #2, but
it's simple, intuitive, and adequate. I'll explain approaches #2 and #3 in separate email messages.

Throughout, there's the orthogonal issue of the keyword or operator which appears in the header for this purpose. "reveal" is perfect conceptually, but given how often we'd need to say it (most methods!), and given the pleasing compactness E has maintained so far, I find "reveal" too wordy. Instead, how about "=>", as in E's existing "maps to" operator. I think

f(x) => y

reads well.

  1. The Declaration Approach

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. If I understand Dean's Joule-derived approach, we might have the "y" declare the resolver for the unnamed promise to be revealed, and then we'd reveal a value by resolving the resolver of this promise:

define f(x) => y { y resolve(x asInteger) }

As Joule does, we could make promise resolution syntactically lighter weight, but we would still need to explain something unfamiliar, or leave it as unexplained magic, in the very earliest examples a programmer would see. Let's return to the assignment approach.

In E, a defining occurrence of a variable also has an optional declaration, so we might think to say:

define f(x : char) => y : integer { y := x asInteger }

For documentary purposes, this reads wonderfully! Unfortunately, there's a fatal problem. In today's E, everywhere a variable is defined, it also comes into existence with an initial value. On the left side of a define, the initial value is provided by the right side. In a pattern-match expression ("=~"), a variable on the right is initialized by the value on the left. A parameter, such as "x", is initialized by its corresponding argument. What of "y"? A standard answer in dynamically typed languages is "null", but that is incompatible with the above "integer" declaration. I can't think of a syntax for putting an initialization expression in the header that doesn't destroy the readability that makes this approach so appealing in the first place.

On other code examples, the declaration approach leads to an explosion of names and less readable programs. Contrast current E's

define PointMaker(x, y) {

     define Point {
         to getX {x}
         to getY {y}
     }

}

with the declaration approach's

define PointMaker(x, y) => p {

     p := define Point {
         to getX => result { result := x }
         to getY => result { result := y }
     }

}

If I'm missing a way to rescue this approach, please let me know!

         Cheers,
         --MarkM