From: jlg@cochiti.lanl.gov (Jim Giles)
Subject: Re: Types, arrays, generality, and notation (was: Fortran90 and Arrays)
Organization: Los Alamos National Laboratory
Date: Thu, 16 Apr 1992 21:15:03 GMT

In article <STEPHEN.92Apr16042133@estragon.uchicago.edu>, stephen@estragon.uchicago.edu (Stephen P Spackman) writes:
|> [...]
|> Consider implementing APL. I'm goig to need a parser, and I'm going to
|> need arrays of complex numbers. Is it *wrong* to want a language
|> that's competent to both? Is it even hard to imagine such a language?
|> It won't look like FORTRAN, APL or Lisp, but I don't see why it
|> shouldn't exist. I don't even see much evidence of its being hard to
|> achieve.

I see plenty of evidence that it's hard to achieve.  Languages which
attempt even a fraction of that degree of flexibility are _very_
unpopular and hard to learn.  Even Ada (which is not nearly as 
complicated as what you are recommending) is most heavily criticized
just for its size.

Strictly speaking, of course, any Turing equivalent language can be
applied in any problem domain.  The issue here is that in any real
language, some things will be easier to denote than others (if you make
arrays as hard to manipulate as musical scores in your language, your
scientific/numerical users _AND_ your musician users will _both_
want additional conveniences installed).  There are an _infinite_
number of possible problem domains - any notational convenience
you introduce above and beyond raw Turing equivalence is in response
to some _subset_ of those potential applications.  The most widely
useful of these conveniences will be common to nearly _all_ practical
programming languages - there are few of them and most users in any
field will ask for the same ones.  Less widely demanded features 
satisfy narrower and narrower application domains - but this is 
the _definition_ of higher level languages: they directly support 
the higher levels of abstraction in the target application areas.

|> [...]
|> Look at what you wrote. You deemed them unworthy of support. Now you
|> want to support them? [...]

I don't know what you mean.  You're the one who has been claiming
things are unworthy of support.  I calim that different things 
are worthy of different levels of support depending on the target
audience of the language.  I never said _anything_ was completely
unworthy of support (except those thing which conflict with better
methods of support for other features).

|> [...]                      Anyway, you can drop arrays from your list of
|> useful primitives.  They're just funtions that return lvalues.

Ha, Ha, Ha, Ha, Ha, Ha, Ha, Ha, ....  Try selling that to users who need
to use them!  A physicist dosn't know or _care_ that these concepts are
equivalent in the abstract thoughts of computing scientists.  A physicist
shouldn't _need_ to know or care about this either.  What he wants is
a convenient notation to represent algorithms of interest to him.  This
does not mean he wants some minimalist notation which satisfies some
esthetic urges of a computing science theorist.

|> [...]
|> But I don't think the issue is whether you can MODEL them. We all know
|> that's trivial but tedious. The question is can I actually USE them in
|> my programmes, and still have my programmes be as readable as if tey
|> were "intrinsic"? Why should the compiler writer have to (or try to!)
|> anticipate every application the language will eventually be put to?

He shouldn't.  He has a much more pressing thing to worry about - he has
to provide features and notations which his customers _WANT_, find 
easy to use, debug, and maintain.  This will automatically lead to 
a variety of languages targeted at different user communities. 
 
A notation for arrays as easy to read and use as the present intrinsic
notations will be another intrinsic notation of _something_.  In fortran,
array indexing is not distinguishable from a function reference with
integer arguments - except that functions aren't usually associated with 
any _extents_ in their domains and cannot have individual elements
reassigned.  Come up with a function notation which allows those
extra things and your function == array idea will be the same as
the present (Fortran 77) special syntax.

Of course, there is still a desire for `whole array' operations and
`array slices', etc. - so your functions will have to be extended to
allow for that legibly before anyone will be the least bit interested.  

|> [...]
|> |Units are an enumerated data type.  Numbers with units are a record
|> |type consisting of a magnitude (possible in some union of numeric
|> |types), a list of numerator units, and a list of denominator units.
|> |Arithmetic operators on this record are easily defined (like: plus
|> [...]
|> (-: So I have to use DYNAMIC CHECKS? What are you, some kind of
|> HEATHEN? ;-)

No.  The user has a choice (just like I keep saying he should).  He
can derive a subtype of what I gave above in which the units lists 
are partially or completely fixed within the type specification.
This uses the concept of subranges of enumerated types - which 
I mentioned in my list (implicitly at least, integer subranges
are a subtype of integer, subranges of enumerated types are also
possible).

This requires a little work on the programmers part. But dimensional 
analysis using static data types is not a common request from users,
so it is alright in my book if it takes a little extra effort.

|> [...]
|> ||> 	type itself
|> |
|> |Recent research has shown this to be a bad idea.  What do you propose to
|> 
|> ???? For instance? How recent? First I heard.

I'll get a list and post it early next week.

|> [...]
|> 
|> Without it, type constructors have to remain "magic" operations that
|> the user can't write or extend. That or you need a whole separate
|> langauage over types, and then you will need a language over THEIR
|> types and you will need.... (which is the situation in ITT, sort of,
|> where you always have to guess which Universe to operate in).

There are languages in which there is a two tiered approach.  OOP
languages are in this camp (the concept of class is manipulated
differently than all other attributes of the environment).  Most
of the capabilities you mentioned (which I've elided for space)
can be accomplished in such a type system.  The main problem is 
type systems in which type is _just_ another type.

|> [...]
|> |With the addition of functions to my original list, these are all
|> |capable of being constructed from the tools I gave.  Once constructed,
|> |the user can give them names and treat them as if they were built-in 
|> |for the rest of the program - just like any other user defined type.
|> 
|> Will they behave EXACTLY as if they were built in, with all the rights
|> and priveleges whatsitting thereto? If so, we have no argument, but I
|> don't believe this is really what you mean.

What rights and privilages do you think are missing?  The only one
I can think of is that the predefined types are predefined, the user
doesn't have to somehow include a definition of them in order to use
them.  This may entail some degree of dependent compilation (so that
separate program components can guarantee they're using the same
definition of a user defined type).  Other than that, what differences
do you suspect will exist?

|> [... processes ...]
|> |aren't a data type but a flow control mechanism.
|> 
|> Their representations are objects in a type or types. Just as with
|> functions (which are control mechanisms if anything is!).

Functions define data relationships, not flow control.  At least,
that's the semantics of them.  In a procedural language, flow control
is used as the implementation of these data relationships. 

Yes, you can use the data structuring tools I listed to describe
the representations of objects which are used by processes.  You can
even pretend these data objects _are_ the processes.  But processes
inherently imply some flow control mechanism (even if it's not
explicitly visible to the user).

|> [...]                            The question is WHAT IS EASY AND
|> USABLE in the *source notation*? Can you provide me with surface
|> syntax for 8:00am that is readable and practical? [...]

I don't know your tastes in "easily readible and practical".  As the
earlier part of this thread demonstrated, there are some people who
find the alphabet soup that J programming requires "easily readible 
and practical".  From this, I conclude that there is a considerable
variation is the definitions of these terms.  

Using Fortran 90 and the extensions we recommend (which include 
allowing user defined postfix operators) we could write 8:00am 
as `8:00.am.'  The postfix operator `.am.' is defined to take an
integer as an argument and return a time (8.am. for example).
The colon infix operator could be overloaded to take an integer 
argument and a time argument and return a time (properly adjusted 
to use the integer as the hour part and the former `time' moved
down to minutes).  User defined unary operators have higher
precedence than any other operators.  User defined infix operators 
have lower.  By defining the colon operator to be right-associative,
`8:01:30.am.' could even specify a minute thirty seconds past
eight in the morning.

|> [...]                                            Can you provide me
|> with enough type system support that I don't need to know if my sets
|> are implemented with arrays or not? *Not even when writing a literal
|> for one*?

Yes and no.  Yes, the _bulk_ of your program needn't know the internal
implementation of sets.  No, because you must (somewhere) define _some_
implementation for them.  Of course, you may do this by USEing a module
which was written by someone else and you've never looked into.

|> [...]             Can I introduce control constructs with the same
|> relationships to my types that IF has to Booleans or FOR has to
|> Integers?

No.  The discussion so far hasn't mentioned control constructs _at_all_
since they are not types, operators, or anything else that's been 
associated with this thread.

|> [...]
|> *I'm not talking about syntax*. I'm talking about choice of
|> identifiers. I said "how do I introduce a new KEYWORD", not "how do I
|> introduce a new PRODUCTION".  [...]

My definition of "keyword" (in common with most) is that it's an
identifier which has built-in meaning at the _syntactical_ level of
the language.

|> [...]                         Why should it be any more confusing for
|> me to introduce a new construction AT <time> DO <stmt> END than it is
|> for me to introduce a new function atDo(<time>, <stmt>)? They're just
|> names, and you define new names.

So, what you want is not to define new keywords at all, you want to 
define new flow control constructs.  And no, our proposal as it stands
does not allow that.  The question is, what benefit does it offer
above what we _do_ offer:

      i = .AT. <time> .DO. <expr> .END.

Since we don't advocate so called "expression languages", a statement
is a different thing than an expression.  Assignment is a statement
level operator, not an expression level one.  Further, an expression
cannot stand alone as a statement - you've got to do _something_
with the value of the expression.  (I suppose you _could_ define
.AT. to be a prefix assignment operator which throws away its 
argument or you could define it to be a procedure call which 
returns no value.)

|> [...]
|> I repeat: I have never once suggested changing the SYNTAX of the
|> language. Not since I was twenty have I made such a suggestion (though
|> there are perfectly good and readable languages that provide some
|> capabilities in that direction, as it happens. I just don't like them,
|> for much the reasons you give: the cognitive load is a bit steep if
|> the programmmer makes a bad choice along the way).

Well, if you can show the need, I'll consider it.  But, as far as I can
tell, there is no need.  It is simpler to implement a language in which
all flow control _syntax_ is fixed within the language design.  For most
procedural languages, flow control is intimately bound to syntax in ways
that types, operators, and variables are not.  This insures (among other
things) that the most common flow control features are properly nested.

|> [...]
|> ||> Integer arithmetic slower than floating point? You're kidding!  [...]
|> 
|> |Yes.  Integer multiplies require the whole product to be formed, for
|> |example.  Many Floating-point implementations do not (though not doing
|> 
|> Would this be hard to fix?

What do you mean "fix".  The floating point implementations I was talking
about are faster because there is _inherently_ less work to do.  I suppose
you could arbitrarily slow them down to the same speed as integers (this
is what you have to do to get absolutely `correct' rounding anyway).

|> [...]
|> But none of this has to do with inherent properties of floating point
|> numbers, it has to do with how the mantissas are manipulated, right?
|> Wouldn't they be more useful unbundled? So they could be used for
|> fixed point and for fractions-of-1 without paying for the often
|> undesirable exponent field?

Unless you want to make the programmer's job lots harder, the exponent
field is not undesirable.

|> [...]                                                    Or, do 
|> |you recommend that _every_ data object has a separate fixed-point
|> |type with a specially tuned scale factor (in which case, every 
|> |operation will be recomputing the scale factors - that is, simulating
|> |float in software).
|> 
|> Yep. Seems to me that the suual arguments about exposing things to the
|> compiler should apply. With a few more bits we could eliminate a lot
|> of the exponent calculations as static. No?

That's not what I meant.  The _programmer's_ job is _lots_ harder if he
must completely analyse the possible dynamic ranges of all his data
objects in order to separately declare each of them to be a different
fixed-point type.  Sure, once the programmer has done so, much of 
the scaling _might_ be optimized out statically.  Having done so, do
you run faster than the easier to program floating-point?  Not much
(if at all).  Is that degree of speedup worth all the effort?  Only
for very important inner loops (where scientific/numerical programmers
often _DO_ disassemble their float representations - but usually for
better precision than for speed).

|> [...]                                                   how could you
|> write BANKING code without floating point....". But I'm still looking.
|> Thanks, incidentally, for the intelligent remarks above.

Gee, I would never use float for banking.  That's one problem domain
where correctness (down to the very last bit) is more important than
speed.  Further, there are no dynamic range problems in banking.  All
calculations are aimed at the same fixed-point precision (usually 
a thousanth of a dollar or less).  Or am I behind the times on 
common banking practices?

J. Giles