[E-Lang] stl-0.8.9k: Syntax Changes

Mark S. Miller markm@caplet.com
Sat, 20 Jan 2001 23:44:57 -0800


The previous note explained the multi-vow when-catch syntax change, which is 
fully upwards compatible from 0.8.9.

I made some changes involving '_', one of which is not upwards compatible, 
but probably doesn't effect anyone's code but mine (and I've already fixed 
mine).


                             "_" vs "any"


The non-upwards compatible change was suggested by Ping.  (Thanks!)  In 
0.8.9, the compact notation for inequality regions (based on Xanadu Integer 
regions) was

    _ > 3

meaning, the region consisting of all integers greater that three.  Similarly,

    _ >= 3 & _ < 7

means the same thing as

    3..!7

all integers from 3 inclusive to 7 exclusive.

These regions can be used as SlotGuards and ValueGuards, effectively 
declaring variables and return types to be constrained to lie with this 
region, like a Pascal subrange.

Why '_'?  Because is sort of looks like a hole.  "_ > 3" is sort of like 
"for all things that fill in the hole, such that the expression is true".  I 
though this was synergistic with the other use for '_' in E, the ignore 
pattern, which matches anything and binds nothing.  This is used wherever 
the syntax demands a pattern, and you "don't care":

    def _ (a :(_ < 7), _) :(_ > 3) { ... a ... }

effectively defines an anonymous closure -- since it declines to name the 
function -- of two parameters, the first of which is bound to parameter 
variable "a" after ensuring that it's an integer less that 7, and the second 
of which will be ignored.  Any value returned will be an integer greater 
than 3.

Since "any" is already a SlotGuard and ValueGuard that accepts anything, 
Ping suggested using "any" rather than "_" for region formation:

    def _ (a :(any < 7), _) :(any > 3) { ... a ... }

Thanks to E's treatment of operators -- they are just sugar for messages, 
"any" has no special kernel-level support, other than to be made available 
in the initial name space.  It is written in E without any special privilege.

There two kinds of hole are clearly different, and I think it's clearer for 
them to look different than the same.  Thanks Ping!  


                 Lighter Weight Syntax for Anonymous Closures


Among lexical lambda languages, a controversial issue is the need for 
macros.  Scheme provides very principled macros, and Scheme programmers find 
themselves needing to use and invent new macros all the time.  Scheme 
without macros is considered painful.

Java has no macros, but those who have experienced macros elsewhere find 
their lack a constant irritation, although on balance many (including 
myself) think Java made the right decision.

Smalltalk also has no macros, and they aren't missed.  There was a time in 
which I was switching between Smalltalk and a language with macros (well, 
sort of.  C++ with the C pre-processor).  Even then, I rarely felt the lack 
of macros in Smalltalk.  Dean was in the same situation, felt likewise, and 
put his finger on the reason.

Ironically, Smalltalk had a significantly lighter weight closure syntax than 
Scheme, so Smalltalk programmers were willing to say, effectively, "lambda" 
when they needed to.  Smalltalk's square brackets are as light, 
notationally, as C's curly brackets, so they could be used, like C's curly 
brackets, for all control abstractions.  Unlike C, including user defined 
control abstractions.  Scheme programmers had to spell it out, unless they 
invented a macro to say "lambda" for them in this specific pattern, and then 
in that one.

E already has language defined C-like curly brackets for a non-extensible 
set of control constructs.  We also have Scheme-like closures that enable 
user-defined control abstractions.  Unfortunately, these are also 
Scheme-like in the notational pain they impose (or rather, that Scheme would 
impose without a macro system).  Part of this is on purpose: E's closure 
definition syntax encourages closures to be named, since anonymous closures 
can't be upgraded.  However, closures used as arguments to sequential 
control abstractions typically live only during the execution of the control 
construct, and therefore, no longer than this turn of the vat.  Therefore, 
these wouldn't be subject to upgrade anyway.

For example, here's a possible abstraction for gathering together the 
elements of a list that pass a supplied predicate:

    define select(list, pred) :any {
        define result := [] diverge
        for x in list {
            if (pred(x)) {
                result push(x)
            }
        }
        result snapshot
    }

No problem here.  Now here's a use:

    def sublist := select(list, def _(x) :boolean { x%%7 > 3 })

Contrast with the Smalltalk:

    sublist := select from: list with: [: x | x %% 7 > 3]

It seems like a trivial difference, but those extra characters means people 
will casually use the Smalltalk one as just another control construct, but 
not the E one.  Likewise, people do not casually use inner classes in Java 
for control constructs, even anonymous ones.

The following new sugaring rules accomplish this, works naturally with the 
rest of the language, is enabled by the above narrowing of the use of "_", 
and (given the above non-upwards compatible change) is fully upward 
compatible from 0.8.9.

* When "_" is used as the name in a function/object definition, then the 
initial "define" may be left out:

    def sublist := select(list, _(x) :boolean { x%%7 > 3 })

* When the initial "define" is left out, if the parts after the "_" are "() 
:any", these can be left out as well.

Experience from Smalltalk is that by far the most common form of anonymous 
closure as control abstraction argument is the no-argument anonymous 
function, also known as the Algol-60 "thunk".  For example, a use of a 
user-defined while loop in 0.8.9 may look like:

     whileLoop(def _() :boolean { ...condition... },
                    def _() :any { ...body...})

whereas in 0.8.9k, one could write:

     whileLoop(_{ ...condition... },
                    _{ ...body...})

This is sufficiently close to the built in control constructs to feel good.

        Cheers,
        --MarkM