Re: K.I.S.S. Factor (Re: Numerics)

"Richard A. O'Keefe" <[email protected]>
Newsgroups gmane.comp.lang.eiffel.smalleiffel
Message-ID <[email protected]>
Dominique Colnet wrote:

	Actually, in most real situations, you would have:
	
How do you know what other people would have "in most real situations"?
Clearly, before the invention of {<type> <stuff>} notation, "in most
(indeed, all) real situations" people couldn't possibly have the
{<type> <stuff>} notation, and equally clearly, in ISE Eiffel they
would be relying on conversions.

Let's try to avoid misunderstandings here.
*My* primary concern is simple numeric literals.
*I* am arguing for a vaguely Haskell-like approach where

 - every expression has a unique principal type determined by its
   form and by the declarations of the identifiers it mentions

 - this type can be determined by a single pass working from the
   leaves towards the root

 - however, that principal type will in general contain constrained
   type variables due to integers (exact size to be determined by
   context), floating point numbers (exact size to be determined by
   context), characters (for characters which can only be Unicode,
   Unicode; for characters which could be Latin-1, exact type to
   be determined by context), strings (for strings any of whose
   characters can only be Unicode, Unicode; for other strings, exact
   type to be determined by context --- note that this entirely
   eliminates the need for the painfully C-like 'U' prefix), and
   arrays (where the choice of ARRAY, FAST_ARRAY, or NATIVE_ARRAY
   or indeed any built-in sequence type would similarly depend on the context)

 - the context of an expression is the variable it is assigned to or
   the method argument it is passed to.  Such contexts cannot contain
   type variables.  Matching the type of the context against the
   parametric type of the expression either succeeds (in a rather mild
   generalisation of the usual Eiffel type checking rules) or fails.
   If it succeeds, binding type variables, the type variables are
   checked to ensure that they satisfy their constraints.  This
   determines the types of the literals.  (The constraints can be set
   up so that if any literal does not fit its context-determined type,
   this is detected by failure to satisfy the constraints.)

 - this involves no conversions.
   Conversions are a separate concern.

	Again, we don't want to create extra TUPLEs.

Again, you give no reason for this.
This has to do with conversions, which are not my primary concern
here.  The main thing I am concerned about is that for all of

    x: ARRAY[INTEGER_8]
    x: FAST_ARRAY[INTEGER_32]
    x: NATIVE_ARRAY[INTEGER_16]

the construction

    x := << 1, 2, 3 >>

should just work.  The right hand side has the unique principal type
"array' (*a, *b) where {*a in A, *b in I(2)}".  The left hand side
provides specific values for *a and *b at compile time, and so the
compiler knows at compile time exactly what type is required and how
to make it.

However, I was continuing an example from a conversion-related thread.
I have to admit the the dictionary example *would* involve creating
some tuples (and an array) which would be no further use after the
conversion feature did its work.

I don't see a problem with that, UNLESS the code in question is in part
of the program that is executed very often AND there is nothing the
programmer can do about it.  We have no reason to expect the first, and
absolute certainty that the second is false.

	I continue to think that we made a huge progress from the old manifest 
	array notation to the new facilities presented in 
	SmartEiffel/tutorial/manifest_notation.e

Please correct the comment that mentions 'tilda' to read 'tilde'.
In the string examples "... its a ..." should be "... it's a ...".

I must say that I find "{ ... }" notation somewhat terrifying.  I could
all too easily begin a string with a left curly brace in all innocence
and get entirely the wrong value.  I would have expected something like
"%-verbatim-%......" or
"%-left-verbatim-%..."
or something that both uses a special % escape to get a special effect
and spells out explicitly what's going on.

As noted above, the 'U' prefix on Unicode strings is embarrassingly like
C's 'L' prefix for the same purpose.  Ada 95 manages without any such
thing, and the "expressions have principal types that may contain constrained
type variables" approach outlined above also manages without any such kluge.
And the approach I recommend can also cope with a range of character and
string types, so that you could handle say
    IA5_CHARACTER				IA5_STRING
    ASCII_CHARACTER				ASCII_STRING
    LATIN1_CHARACTER	(= CHARACTER)		LATIN1_STRING (= STRING)
    UNICODE_BMP_CHARACTER			UNICODE_BMP_STRING
    UNICODE_CHARACTER				UNICODE_STRING
_without_ needing a corresponding range of magic prefixes.  You _will_ have
to extend the range of character and string types, because the current
16-bit "Unicode" strings can't possibly handle Unicode 4's 98-thousand-odd
characters.

"A 8 hexadecimal" should be "An 8 hexadecimal"
"A(n) {n} hexadecimal digit denote an INTEGER_{width}"
should be "A(n) {n} hexadecimal digit literal denotes an INTEGER_{width}"
"beeing" should be "being"
"ommit" should be "omit"

The {INTEGER_<size> <whole number>} and {REAL_<size> <whole number>} thing
is, for _that_ purpose, clumsy and unnecessary.  It doesn't look like
'huge progress' to me.

The ability to set a lower bound in an array literal IS a step forward,
in the sense that it gives you a way to do something that you could not
do with << >> notation.  However, the _way_ it does it is, um, confusing.
Yes, confusing's the polite way to say it.  Confusing.  Something of the
kind could have been done for << >> notation:
    '<<' [<lower bound> ':'] [<element> {',' <element>}] '>>'

The ability to provide values for other built-in types is also definitely
a step forward, although the ARRAY2 notation has to be seen to be believed.
(I was certainly unable to predict it from the ARRAY notation.)

However, this is where the argument for conversions comes in (although I
can live without them):

    we have here a highly elaborate and apparently unsystematic notation
    for a subset of built-in types, and ONLY built-in types.  There
    remains no corresponding way to describe values of other built-in
    types (if there is a way to write {TIME ...something...} then it's
    a pity tutorial/manifest_notation.e doesn't give an example) nor
    any way for people to similarly describe values of their own types.

I have to agree that there is a down-side to conversions.
But at least they are *one* mechanism to do everything that the extended
manifest notation does and far more, *without* requiring the mastery of
elaborate ad-hoc syntax.

Note that nothing in anything I have written should be interpreted as
saying that that {<type> <literal>} notation should be *removed* from
SmartEiffel, only that it shouldn't be *needed* by the average programmer,
at least for simple things like numbers and sequences.

	> Are we talking about the same language?  Eiffel is a language where the
	> most simple obvious way to write a string results in a run-time copy.
	I don't understand.

Why, tutorial/manifest_notation.e mentions it!
Here's a sample class:

class STEST
creation
   make
feature
   make is
      local
         x, y: STRING
         b:    BOOLEAN
      do
         x := ""
         y := ""
         from b := false until b loop
            x := y
            y := "fred"
            b := not b
         end
         if x.to_pointer = y.to_pointer then
            io.put_string("They are the same.%N")
         else
            io.put_string("They are different.%N")
         end
      end
end -- class STEST

Compile it and run it, and the output is 'They are different.'
In the assignment 'y := "fred"' we see 'the most simple obvious way
to write a string', that is, between double quotes with nothing extra,
and it certainly 'results in a run-time copy'.  What's hard to understand here?

(Who was it who wrote
    Adding immutable (constant) strings would be a significant improvement,
    both in terms of performance (potential optimiszations) and
    expressiveness (and safety)
and where did they write it?)

	> Eiffel is a language where all array accesses are range checked (at
	> least in principle) _without_ the compile-time bounding of integer

	Wrong while using the -boost compilation mode.

I think you have missed the point here.  When -boost omits the checks,
this is *unsound*.  Languages which really *are* designed for performance
can omit the bounds checks *safely*.  I specifically mentioned Pascal and
Ada to clarify this.

	BTW, our point is that the most important goal of assertions is
	for code documentation and to debug (to be short).

Silly me.  Here was me thinking that the most important goal of
assertions was TO HELP THE PROGRAMMER PRODUCE WORKING PROGRAMS.

	> Eiffel is a language without a standard binding to the BLAS.
	Right. There are no standards for Eiffel. Sad.

Well, ETL has to a certain extent acted as a standard.  The point at issue
is not whether or not there's an international standard, but rather that
if performance really *were* a primary goal of the Eiffel community in
general, there would be a "de facto" binding to the BLAS, and if it were
a primary goal of the SmartEiffel developers, I'd expect there to be one
for SmartEiffel even if there weren't a de facto one.

Don't get me wrong.  I am not trashing SmartEiffel here for not providing
the kinds of performance features I get in C and Fortran95 and Ada.  If I
didn't think that reducing cost over the whole lifetime of a system were
not more important than performance simpliciter, I wouldn't be interested
in Eiffel.  I *don't* think that performance should be the primary goal.

	You cannot speak seriously of Eiffel I/O.

Yes I can.  I just did.  

	There is no Eiffel I/O.

Yes there is.  ELKS 95.  Class FILE is described at
    http://archive.eiffel.com/doc/manuals/library/elks/file_fm.html
(at least, that's what my browser is showing me in the URL box).
Class STD_FILES is described at
    http://archive.eiffel.com/doc/manuals/library/elks/std_files_fm.html
I take it that ELKS 2000/ELKS 2002 was effectively replaced by work on
an ECMA standard, but the NICE web site does still list ELKS 95 as one
of the 'standards'.

	There is an io _library_ we provides.
	And you can say that this library is bad but not that Eiffel is bad.
	A library is not a language.

True.  A library is *PART* of a language.  The manifest notation in
SmartEiffel makes this crystal clear:  the compiler is in fact very
tightly bound to a large chunk of the library.  (The compiler has to
know exactly what to do to make a HASHED_DICTIONARY[INTEGER,STRING]...)
In the C, C++, Ada, Fortran, Pascal, and Smalltalk standards -- the only
ones I have ready to hand -- it is made quite clear that the standard
library is part of the standard language and a compiler is entitled to
have hard-wired knowledge about the things in the standard library.

One can certainly speak of the overheads *implied* by a library.

	It is easy to reach the C speed by calling the correct C I/O
	function from Eiffel.
	
Which simply emphasises my point:  if you want performance, bypass the
stuff you get from SmartEiffel and go straight to C.  THIS IS *FINE*.
For me it's not a problem.  Remember, Eiffel is interesting for cost
over the whole life cycle (or rather, it *may* be interesting for
reducing cost over the life cycle on the day when there *is* a stable
standard) and it is perfectly OK if the performance doesn't match C.
If top performance really were a goal of the SmartEiffel team, we'd
see a very different I/O library.  We don't, so top performance CAN'T
be a high priority goal of the SmartEiffel team.  And that's *FINE*
because the performance is *good enough* that other things are more
important at this stage.

	> It doesn't really matter if some statement creates a dozen, or a thousand
	> objects, provided that statement is not executed very often.
	For you but not for us.

I really don't get you here.  If a statement creates a few dozen objects
but is not executed very often, the *time* cost will be a few milliseconds
and the *space* cost will be a few kilobytes out of a run time that may be
hours and a space cost that may be gigabytes of total allocation.  It's
up to me as a programmer to decide whether I want to use a really clear
syntax which may have some overheads or to write 'tuned' code, just as it's
up to me as a programmer which algorithm to use, a simple obvious one that
I can get right quickly or a fancy one that's much harder to get right but
more efficient for large problems.

Again, every time a simple string literal is encountered at run time, it
is copied, and yet somehow *that* doesn't seem to worry you.  (It worries
you enough to provide 'once "..."', but not enough to make that the
default behaviour or to have the compiler emit warnings about all the
hidden copying.)

	Again, compare the old situation to the new one.

I have!  I used to keep up with SmallEiffel; when SmartEiffel introduced
the idea that 1 and 1000 had different types I protested and then stopped
keeping up for a while in the hope that this botch would go away.

	We made many progresses.
	
You have done a very great deal.  But with respect to numeric constants,
you have made life harder for programmers, not easier, and you broke a
lot of code, mine included.  I keep on pointing out that you don't _need_
this elaborate syntax for things like integers, reals, characters, strings,
and sequences.

	Anyway, I have the feeling that we won't find an agreement ;-)

I am a little more sanguine here.  The approach I recommend is proven
technology, efficiently implementable in a compiler, with ZERO run-time
overheads, would not break any currently legal code, and would not
preclude (although it would not require) supporting conversions.
I live in hope that I might eventually be sufficiently persuasive.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.