[E-Lang] multiple-domain language, plus Super Newbie is lost without "The Dot" (was: down with `define' (was: newbie syntax: picayune points from a prejudiced programmer))

zooko@mad-scientist.com zooko@mad-scientist.com
Thu, 01 Mar 2001 23:58:56 -0800


[Sorry about zooko.com bouncing.  Fixes are wending their way through
the depths of DNS.  <zooko@mad-scientist.com> still works.]


Wow!  Your willingness to simplify syntax is great!



I.  multiple-domain language: shell, rapid prototyping, engineering


I'm still chewing on the idea of E as a shell language, rapid
prototyping language, and Real Engineering language at once.  It is
intriguingly ambitious, and clearly would have great benefits.


But it seems to me that you are overlooking the biggest obstacles:
required "def" for each variable and required ":any" type-annotation
for each function.


To me (and I do write a *lot* of shell scripts in bash, mostly
interactively but also in persistent files), typing a whole 'nother
word in such frequent cases as those two would be a real strain.
(Contrast with appending `()', or inserting `.' -- I do this without
thinking about it and I don't mind.)


I read some (all?) of the e-lang list threads about the ":any" "reveal
operator", but if I were a naive newcomer I would not have read those
threads, and I might well prefer to have an explicit, required `return'
keyword, which is perfectly familiar to all C, C++, Java and Python
programmers, and thus make type annotations optional, instead of having
expression-style, which is not familiar and which I don't like anyway.


[Note:  the real Zooko does not make pronouncements about subtle
matters of taste in language design without first trying it on for
size, but the picky newbie who is currently performing a
Man-In-The-Middle attack on Zooko's net persona hates expression-style,
which doesn't appear in C, C++, Java or Python, and he hates repeatedly
typing ":any" even more, even though he's never tried it.]


If you target E (or a variant of E...) solely at the "real engineering
languages" market, then I'll regard the lack of "return type inference"
as a minor regret, but if you target it at shell scripting and
rapid-prototyping, then I'll regard it is a significant impediment.



II.  syntax to help illiterates and speed readers


> 5) I don't think command line completion can provide the Java-like 
> invocation dot.  Therefore, and because #2 remains in the pocket, and also
> because the invocation dot simply makes my eyeballs hurt, E continues to use 
> whitespace rather than dot for invocation.  No change on this one.


I'd like to reaffirm my preference for invocation dots.  I have two
reasons, neither of which is simply a matter of taste.  (But I would
like for someone to tell me if my feelings on the second point are at
all reasonable or widespread.)


1.  Most importantly, it is familiar to C++, Java, and Python.


2.  Secondly, I like for syntax to explicitly show me the syntactic
facts of the code so that I needn't actually understand the words.
There follows a snippet of E code which has been munged to demonstrate
this point.


What if you were scanning, so that you didn't really read all the
words?  Or equivalently, what if you were a total newbie, and you
hadn't learned the E syntax at all, but you were trying to "get a feel"
for some source code.  In both cases you may temporarily or even
permanently confuse keywords and other words.


I've taken a snippet of E code from the E distribution and changed all
of the words whose meaning is not known to me in my current E-ignorant
and sleep-deprived state.  The only words remaining are English
function words (`to' and `if'), as well as the common programmer's
counter variable `i' and the constants `true' and `false'.  Also 
I included `def'.


to wordTwo : wordthree {
    def wordfive := [] wordsix
    if (!wordSeven) {
        if (wordEight == 0) {
            wordfive wordnine(WordTen(false, []))
        } else {
            wordfive wordnine(WordTen(false, [wordEleven[0]]))
        } }
    def i := worldThirteen
    while (i < wordEight) {
        wordfive wordnine(WordTen(true, wordEleven(i, wordEight wordFourteen(i+2))))
        i += 2
    }
    wordfive wordFifteen
}


I find it difficult to quickly scan through this code while mentally
noting each of the method invocations.


Some of these combinations leave me confused even after thinking
about them.  What about `[] wordsix' -- is that a method invocation?
Or `wordfive wordFifteen'?


Having parens required after function invocations clarifies for the
newbie who isn't sure what is a keyword and what is not, but it does
not make "scanning for whitespace patterns" work, as `def wordfive' and
`[] wordsix' still look, for an instant, like `objname methname'.


Contrast with the following copy in which I've added dots in what 
I think are the only method invocations.


to wordTwo : wordthree {
    def wordfive := [] wordsix
    if (!wordSeven) {
        if (wordEight == 0) {
            wordfive.wordnine(WordTen(false, []))
        } else {
            wordfive.wordnine(WordTen(false, [wordEleven[0]]))
        } }
    def i := worldThirteen
    while (i < wordEight) {
        wordfive.wordnine(WordTen(true, wordEleven(i, wordEight.wordFourteen(i+2))))
        i += 2
    }
    wordfive.wordFifteen
}



Now here's the actual snippet.  I actually *did* suffer from this
problem (lack of dots and parens) when trying to write this e-mail, as
I couldn't tell whether `diverge' and `snapshot' where methods being
invoked or keywords, so I wasn't sure whether to prepend dots!  
I decided that they were keywords for three reasons:

1.  `diverge' and `snapshot' sound suspiciously like language-level semantics.
2.  `diverge' appears after `[]', which is probably an object but which
    might a special kind of thing that needs some kind of keyword
    applied to it.
3.  I searched through the rest of the file and didn't see definitions
    of those words.


Hm.  Actually I'll bet `snapshot' is a method because it comes after an
object.


to getIntervals : any {
    def flex := [] diverge
    if (!myBoundedLeft) {
        if (myLen == 0) {
            flex push(OrderedRegionMaker(false, []))
        } else {
            flex push(OrderedRegionMaker(false, [myEdges[0]]))
        }
    }
    def i := myInParity
    while (i < myLen) {
        flex push(OrderedRegionMaker(true, myEdges(i, myLen min(i+2))))
        i += 2
    }
    flex snapshot
}


Regards,

Zooko

P.S.  It is my long-standing policy not to post when sleep-deprived
(not to mention posting when ignorant).  I'm violating this policy in
order to bring you this live and uncensored demonstration of just how
dumb newbies can be about programming language meaning.

P.P.S.  I think maybe required-parens, which MarkM has already blessed,
would solve the problems demonstrated herein.

P.P.P.S.  The required parens around `(!myBoundedLeft)' and 
`(i < myLen)' make it harder for my tired eyes to see what is going on.
I used to put parens around all such conditional expressions in my
Python code (where they are optional), but a co-worker struck a deal
with me in which I would stop doing that if he would stop doing
something that was irritating me, and since then I've grown to much
appreciate the cleanliness.  Here then is my final preferred version,
though I'm wavering a bit on "dots".


to getIntervals : any {
    def flex := [] diverge
    if !myBoundedLeft {
        if myLen == 0 {
            flex.push(OrderedRegionMaker(false, []))
        } else {
            flex.push(OrderedRegionMaker(false, [myEdges[0]]))
        }
    }
    def i := myInParity
    while i < myLen {
        flex.push(OrderedRegionMaker(true, myEdges(i, myLen.min(i+2))))
        i += 2
    }
    flex.snapshot()
}


Hm.


Okay, now I can't resist making one last syntactic change just to see
how it'll look...


to getIntervals : any:
    def flex := [] diverge
    if !myBoundedLeft:
        if myLen == 0:
            flex.push(OrderedRegionMaker(false, []))
        else:
            flex.push(OrderedRegionMaker(false, [myEdges[0]]))

    def i := myInParity
    while i < myLen:
        flex.push(OrderedRegionMaker(true, myEdges(i, myLen.min(i+2))))
        i += 2

    flex.snapshot()


A-ha!  It's Python!


:-)


Regards,

Zooko