[E-Lang] down with `define' (was: newbie syntax: picayune points from a prejudiced programmer)

Mark S. Miller markm@caplet.com
Sat, 03 Mar 2001 10:34:04 -0800


At 03:50 AM Saturday 3/3/01, Ka-Ping Yee wrote:
>> Waddaya think?
>
>In all, fantastic.  Some of these simplifications are things i've
>wished for for a long time.  The others all seem sensible.

Thanks!

>One note:
>
>> c) The thunk shorthand:
>> 
>>     _{ ... }
>
>Flagging a thunk with "_" is, in my opinion, just silly.

Agreed.  We got there by a historical derivation that was sensible at the 
time.  (See "Lighter Weight Syntax for Anonymous Closures" in 
http://www.eros-os.org/pipermail/e-lang/2001-January/004123.html .)  I quote 
the beginning of this section below to set context (but it's also good to go 
back and read the original):

I wrote:
>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, [...]
>
>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 [...],
>Even then, I rarely felt the lack of macros in Smalltalk.  [...]
>
>[...] Smalltalk ha[s] a significantly lighter weight closure syntax than 
>Scheme, so Smalltalk programmers were willing to say, effectively, "lambda" 
>when they needed to.

A concrete example in E is "require", which is like C's "assert" except to 
switch turns it off.  It is defined as:


    def require0 {
        to (cond, thunk) {
            if (! cond) { throw(thunk()) }
        }
        to (cond) {
            require0(cond, _{"required condition failed"})
        }
    }

(Don't worry about "require" vs "require0".  This is needed to work around a 
bootstrapping problem is seeding the universalScope.)

And it's used as

    require(i >= j, _{`$i must be smaller than $j`})

If the condition is false, then the quasi-literal string expression is never 
evaluated to an actual string.  Similarly for more expensive expressions we 
may write to give ourselves better diagnostics.

When we adapt Brian Marick's tracing system 
http://www.erights.org/elib/Tracing.html for use from the E language, this 
issue will be pervasive.  In both cases, extra syntactic cost means either 
fewer diagnostics, or diagnostics that are cheap when not computed.

For a completely different example, the new Kernel-E looping construct is as 
naturally used:

    loop( _{ expr })

as many user-defined looping constructs would now be.


>This should be explicit -- it should say
>
>    thunk { ... }
>
>or -- in grand Scheme and Python tradition --
>
>    lambda { ... }

That's why neither of these work for me, but...

>You could also consider "def { ... }", i suppose.  

...might.  It sounds silly, but the Scheme vs Smalltalk experience says 
these extra characters might make the difference.

Although I seem to be contradicting myself, I don't much care about the 
length difference between "def" and "_".  My problem with using "def" for 
this is simply that it does not define any names, and unlike "def _ ..." it's 
not a name defining construct that you can look at and say "Ah, there's a 
placeholder here instead of the name."  Nevertheless, I still find it 
plausible.


>This permits
>"def :any { ... }" etc.  (No name, no arguments means -- no name,
>no arguments!)

This would make "def" a fatal choice, since its only required syntactically 
lightweight use is a value-returning thunk used as a conditionally evaluated 
value, as shown above for "require".

Consider also the security-useful variant:

    once(_{ expr })

whose thunk-like value can be called at most once.  Or the interesting

    lazyPromise(_{ expr })

that returns a promise for the value of expr, but only evaluates expr 
(once) should anyone send any messages to this promise.  The promise would 
then resolve to that value.  If expr throws an exception, the promise 
becomes a broken reference.  All this can now be coded in unprivileged E.

There are probably a dozen other idioms in this same category that I haven't 
thought of.


Therefore, we can't adopt a syntax that implies the usual ":void" default.


>As a side note, i'll mention that the "lambda" keyword in Python
>has finally come to really mean "lambda", pretty much.

Doesn't seem like a part of the design space that admits a "pretty much".  
No smooth hills to climb, only scraggy cliffs, deep ravines, and telephone 
poles.  In what ways is Python's "lambda" still not lambda?


        Cheers,
        --MarkM