The story of E, part 2 (fwd)

Ka-Ping Yee ping@lfw.org
Sun, 11 Oct 1998 03:27:19 -0700 (PDT)


Hi again.

I apologize in advance like sounding whiny and nitpicky about
small details.  All these are probably much less important than
the security and implementation issues surrounding the language,
but i hope that my suggestions will help to produce something
likable and popular.

On Fri, 9 Oct 1998, Mark S. Miller wrote:
> 
> Thanks.  Except for the color, these will be fixed with the next website
> update.  Care to suggest an alternate color?

I think you're fine without any colour, actually, but if you
want one, a subdued orange or green or burgandy might do.
 
> >> 	(x*x + y*y) doubleValue sqrt
> >
> >Kind of yucky.  How about toFloat?  (like toString)
> >...and toFoo for casting methods in general?
> 
> Wrt casting in general, I'm inclined against the "toFoo" approach because
> it requires a named method per type you can cast to.

Oh, on the general issue, i'd agree with you on that.

However, it does make me wonder how you implement casting behaviour...
if you associate it with the target type, how does the target type get
the information it wants out of the object without a prearranged
interface?

> How about if
> type-objects (including java classes) by convention are also coercers for
> converting from nearby understood types.  Waddaya think of
> 
> 	double coerce(x*x + y*y) sqrt
> 
> or even
> 
> 	double(x*x + y*y) sqrt
> 
> ?

I think this looks great.  Many people, myself included, would find
the latter quite familiar (cf. C++, Perl, Python) and natural.

> But ignoring the general coercion issue and concentrating on this case, I
> like your suggestion but I think I like "as" better than "to":
> 
> 	(x*x + y*y) asDouble sqrt

That's fine too.  It actually reads smoother.  (I only picked "to"
due to "toString".  If i had the choice i would prefer "asString" too...)

> Jeez I wish they hadn't defined "float" to mean "single precision IEEE
> floating point"!  I know of no remaining short word that simply means
> "floating point" without specifying a precision.

Well... i actually used "toFloat" in my original answer because
"double" sounds even more precision-y than "float".  If you don't
want to specify a precision, i think "float" sounds more general,
even though it's used by C.  <shrug> Both are used by C, anyway...

"fp" is a decent abbreviation for "floating point" but you can't
really read it aloud very well.  Can't think of any others.

Where would the type names actually appear in E programs?

(Leading to, "Do i really hafta type out 'Character'?  Could
we just make the type name [and coercing function] 'char'?)

Other coercion issues:

    ? char("abcd")           # should this be allowed?
    # result: 'a'

Possibly convenient, though i don't think it would be necessary
as long as you can "abcd"[0].  I hope you consider strings to be
sequences of characters with all the usual sequence methods:
this is extremely convenient in Python (and *not* doing this is
an almost unbelievable oversight in Perl).

    ? char(97)               # should this be allowed?
    # result: 'a'

    ? int('a')               # should this be allowed?
    # result: 97

This seems nice.  I like it, but if you think it is too easy to
confuse int('4') with int("4") i could relent.  I assume
string('a') yields "a".

    ? 'a' ord
    # result: 97

Another way to get the ordinal value of a character?

Can you easily generate number and character ranges?
Python has the built-in "range" function, which can only
generate ranges of integers (allowing a specified starting
and ending point and positive or negative step).  Perl has
the ".."  operator, such that 1..4 yields (1,2,3,4) and
's'..'u' yields ('s', 't', 'u').

    ? int("4")
    # result: 4

    ? float("4.3")
    # result: 4.3

    ? float(5)
    # result: 5.0

It would be really nice to have conversion right here
in the language instead of having to resort to a library
for atoi and atof conversions.  I would expect float("4.3")
to be consistent with str(4.3) or 4.3 toString.  Converting
from strings in Java was a nightmare for me, partly caused
by the lack of a convenient string converter (without
importing all of the formatting classes) and exacerbated
by the silly chasm between double and Double:

    double length = new Double(lengthfield.getText()).doubleValue();

Ridiculous.  In Python this would be

    length = string.atof(lengthfield.get())

I would like this to be simply

    length := float(lengthfield get)

("lengthfield" is a UI text entry component.)

Is it true that "abc" + 'd' == "abcd" and 'b' + 'c' == "bc"?
What about "-" * 8 == "--------" (useful formatting idiom in Python)?

Actually, in general in Python, any sequence works: [3,5,6] * 3 ==
[3,5,6,3,5,6,3,5,6].  Very convenient for initialization.  If you
want to reserve '*' so that vectors can [3,5,6] * 3 == [9,15,18],
you could consider [3,5,6] rep(3) == [3,5,6,3,5,6,3,5,6].  In Python
this doesn't seem a problem, though, since * repeats heterogeneous
tuples but multiplies only homogeneous vectors, and the repeating
behaviour is almost exclusively used for initialization, which you
do by passing in a tuple anyway.

What are the "standard tuple" and "standard mapping" interfaces?
I'm guessing get(key), put(key), del(key), len(), add(tuple/map)?
Anything else?

Any way to get an escaped copy of a string?  In Python there
is repr(), which produces:

    >>> s = "foo\tbar\n"

    >>> s
    'foo\011bar\012'

    >>> print s
    foo     bar

    >>> repr(s)
    "'foo\\011bar\\012'"

Notice that repr(), not str(), is used for printing out results
in the interactive loop.  This means that you can directly cut
and paste simple objects back into the editor, which can be very
useful (numbers and strings, as well as lists, tuples, and
dictionaries to any depth as long as they contain only numbers
and strings).  In Python all objects support both str() and repr().

This is really good for strings, and provides a sometimes useful
distinction between a shorter display of an object and a complete
representation of its state allowing reconstruction.  (You can
define your own __str__ and __repr__ methods for Python to call.)
It can sometimes be a bother when it is impossible or undesirable
to display complete state, in which case you end up defining
__str__ and __repr__ to just do the same thing.

What is the default string conversion when toString is not given?

Oh, and while we're on the subject of basic operations...

    ? 5 % 3
    # result: 2

    ? -1 % 3
    # result: 2        # what i want (Python & Perl & Tcl agree)

    # result: -1       # arrgghh!  what is WRONG with these C people?!?!

Please please please!  (Possibly related: what is the difference
between % and %%?)

And what is _% ?  I saw it in http://erights.org/doc/e/satan/index.html
under the "Begin" section.

Is there a really good motivation for providing the aliases
"-" -> "subtract" or "negate", "!" -> "not", "~" -> "complement",
"[]" -> "get" etc. as seen in the "call" and "method" sections of 
http://erights.org/doc/e/e-grammar.html?  I am afraid this will
just lead to confusion, especially subtractions which could look
like "object<--(arg)", and only enlarges the grammar.

> In the kernel language, yes, only the curlies of the object expression
> encloses a behavior definition.  In the full syntax, braces either enclose
> a scoped expression, or they enclose a behavior definition, to be learned
> on a case by case basis.  Sorry, but nothing else seemed simpler.  (Feel
> free to take this as a challenge.)

Hmmm... all the ones i saw seemed to create new scopes.  What
are the exceptions?

I notice that "if" and "for" etc. all seem to *require* braces
after them, according to the grammar page (you can't just have a
single expr on its own like in C).  I think this is good.  This
does imply, though, that the parentheses around the condition
are unnecessary -- we could get rid of the extra punctuation in
"if", "for", "while", "switch" to match "escape", "match", etc.

(Though, by the way, i don't understand how the "verb" in "for"
is supposed to work.)

I also don't understand the expansions for "||" and "&&" on the
grammar page.  I would have expected something like

    left || right

->

    define __temp__ := left
    if (__temp__) { __temp__ } else { right }

and

    left && right

->

    define __temp__ := left
    if (__temp__) { right } else { __temp__ }


> I'm just not going to take the grammar into that kind of ambiguity-tempting
> territory.  Let's learn from C++'s mistakes and forget angle brackets.  I'm
> inclined to keep square brackets for tuples -- their visual rigidity is
> appropriately suggestive.  Similarly, I like curlies for mappings and sets.

Conceded -- given the compelling argument for { } around sets, and
especially given the use of [ ] in patterns.

> Btw, if x and y are sets, 
> 	x & y is their intersection
> 	x | y is their union
> 	x - y is the elements of x not in y
> 	x ^ y is the elements in exactly one of them, and
> 	x <= y is true is x is a subset of y.  (likewise with <, >=, and >)

This is very clever and convenient.  I like it.

> So, with all that said, how about I break down and introduce the keyword
> "set":
> 
> 	set {"a", "b", "c"}

I was about to complain that introducing the shorthand for sets made
lists and mappings look too similar, but this solves the problem,
leading to...

On Fri, 9 Oct 1998, Dan Bornstein wrote:
> If you instead introduced a "set" or "map" keyword, then I think you'd still
> be able to unambiguously differentiate between lists, maps and sets:
>
>   [v1, v2, v3]
>   set [v1, v2, v3]
>   [k1 -> v1, k2 -> v2, k3 -> v3]

I have to say i really like this idea a lot.  It's all pretty clear
and keeps the braces nicely reserved for code blocks.

Despite how natural {1, 2, 3} looks for a set, i really think that
set [1, 2, 3] is a perfect compromise.  The only issue, i suppose,
is that you have to look ahead for the -> (or =>) but you have to
do some pattern-matching to turn the above into tupleMaker calls
anyway -- i think it's worth it.

Any particular reason to choose -> or =>?  The only one i can think
of is in favour of "=>" -- harder to confuse with "<-".

> Btw, this latter will in turn be syntactic sugar for:
> 
> 	MappingImpl make([["a", 440], ["b", 493], ["c", 523]])
> 
> where, for example,
> 
> 	["a", 440]
> 
> will be sugar for
> 
> 	tupleMaker make("a", 440)

Why are MappingImpl and tupleMaker differently capitalized and
why does MappingImpl not end in "Maker"?

What is the complete list of globally-available helper objects?
Can you screw up a program by replacing these objects?

> >Does "@" bear any resemblance to a scribble to you?  Does
> >"scribble" at all connote "taking a copy to scribble on" to you?
> >
> >I forget, do you use "@" in quasipatterns to denote a place
> >to write things into?
> 
> I feel no need for special syntax for
> newMutable/scribble/editcopy/editCopy, a good message name would be
> sufficient.  And you're right, "@" is reserved because of its use in
> quasi-patterns.

What about the issue of how you print out mutable mappings and tuples?
I still think some sort of distinctive punctuation could make a good
way to indicate mutability.  Or were you planning to print out, e.g.
"[1,4,5] newMutable" for a mutable tuple?  That seems kind of clunky.

> Well, it'd have to be "editCopy" or I would no longer have a memorable rule
> for when to midcap.  I think I like "editCopy", it's now tied for first
> place with "newMutable".  I would not have been able to guess what
> "scribble" meant.

Would you consider "freeze" and "thaw"?  (Sorry for all the suggestions,
but i seem to be very partial to single short words and i thought it
wouldn't hurt to toss out what i came up with.)  There seems to be a
certain strange aesthetic in me that would like to be able to write a
program without being forced to use a capital letter by the language.

> static methods of Sugar classes define, from E's point of view, instance
> methods of the class they sugar.  The first parameter of the static method
> is the receiver, and the remaining parameters are the parameters of the
> apparent instance method.

I see.  Python does its slices with "__getslice__" and
"__setslice__" methods.  I would like you to consider the sugar
methods "getslice" and "putslice" -- they make a nice parallel
to "get" and "put", and if expanded from [x:y] notation in the
language grammar would be really useful.

> 	? list[list size -1]
> 	# value: 7

I use the negative indices quite often; they're pretty handy.

> >    ? list[-3:]                  # get last three elements
> >    # value: <3, 6, 7>
> 
> 	? list slice(list size -3)
> 	# value: [3, 6, 7]

Not bad, but there is more reading overhead, and it can get
really wordy if "list" is replaced with some expression or
returned value... which you might or might not care about
evaluating twice.
 
> >    ? list := @<1,2,3,4,5>
> >    # value: @<1, 2, 3, 4, 5>
> >
> >    ? list[1:4] := <8>           # slice assignment accepts <> or @<>
> >    # value: <8>
> >
> >    ? list
> >    # value: @<1, 8, 5>
> 
> Nothing like that in E.  Do you find it a great convenience?  Do you
> actually use it?

Yes, i do use it, though admittedly not extremely often.  When
i do use it i am quite thankful for it.

I just like Python's slice notation a lot because it can express
a lot of list-manipulation ideas -- append, insert, slice, delete,
replace -- with one consistent orthogonal notation.  Perl tries
to subsume many of these under "splice", but quite clumsily so.

Because Python's slices don't complain on out-of-bounds indices,
they make it really convenient for me to just check things without
getting paranoid about catching exceptions:

   if word[:1] == "a":    # does this (possibly empty) word start with 'a'?
       ...

You may not like this, if you believe a slice should guarantee a
resulting sequence of a predictable size, but i just wanted to
note one common mode of usage.

> Ask me about Ropes sometime.

Okay.  What are Ropes?

> Here's a close & cleaner (ie, side-effect free) version:
> 
> 	? list := list slice(0,1) + [8] + list slice(4)
> 	# value: [1, 8, 5]
> 
> Since "list slice(0,1)" is statically known to the programmer to be a
> single element, this particular case could have been written
> 
> 	? list := [list[0], 8] + list slice(4)
> 	# value: [1, 8, 5]

Eww.  But you shouldn't have to think about that.  Imagine what
you'd have to write if the list indices were variable quantities...

    ? list[x:y] := z

<=>

    ? list := list slice(0, x) + z + list slice(y)

<=>

    ? list := list slice(0, x) add z add (list slice(y))

That's four method calls and potentially evaluates "list"
twice if it's an expression, or at least looks it up twice.
Expanding that to

    ? list putslice(x, y, z)

might be more efficient.

Establishing a get/put convention this way also gives people
a place to put other kinds of methods to get/put part of an
object (e.g. getrow/putrow for a matrix; get/put for an image
where the two bounds are coordinate pairs, etc, etc.)

Any particular motivation that made you go with get/put
instead of get/set?  No objection here, only curiosity.


Ping                                            Talk back to the Web!
<ping@lfw.org>                                       http://crit.org/