From: jlg@cochiti.lanl.gov (Jim Giles)
Newsgroups: comp.lang.misc
Subject: Types, arrays, generality, and notation (was: Fortran90 and Arrays)
Date: 9 Apr 92 00:31:32 GMT
Article-I.D.: newshost.1992Apr9.003132.5175
Organization: Los Alamos National Laboratory

In article <ROCKWELL.92Apr8164502@socrates.umd.edu>, rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell) writes:
|> Jim Giles:
|>    My example didn't copy _any_ elements `in place'.  The destination
|>    locations were completely disjoint from the source locations.
|> 
|> What does completely disjoint mean?  It's my understanding that you
|> were modifying storage in the very array you used as an argument.

      A(0: :2) = A(1: :2)

You'll notice that no element on the right side of the assignment
is on the left and vice-versa.  The example mostly exhibits that 
arrays can be referenced with stride in a natural way on _either_
side of an assignment.  The fact that I used the same array on 
both sides is incidental to that main point - but it does demonstrate
that such a thing is easily done.

Your J version of this operation was _very_ clumsy:

      A =: 2 # ((#A) $ 0 1) # A

How would this have been different if I had used the example:

      A(0: :2) = B(1: :2)

Are you saying that the J version of this different expression is lot's 
cleaner?  Why?  I suspect that the J version of this is actually _worse_
since duplicating elements is no longer a viable option.  You must shuffle
the odd elements of the existing A value with the _new_ even elements
somehow.  This means that A probably still appears on the right hand
side of the assignment in the J version - even though it's only being
assigned to and not referenced at all in the Fortran version.

|> [...]
|>    The word `type' means any static attribute which is associated with
|>    an identifier in such a way that _every_ occurrence of the
|>    identifier in a given scope has that attribute.
|> 
|> Why is type a property of an identifier, and not a property of either
|> a function or of data?  You're saying that Fortran is typeless except
|> for identifiers?

Ok.  Let me redefine.  `Type' is any static attribute which is associated
with a given denotation (that can be an identifier, literal, function, or
even an expression) that can be used by the user and the compiler
for consistency checks and/or optimization.  Unless you want to write
_all_ programs using only literals and intrinsics, you must name _some_
of your functions and data.  The issue is whether any attributes are
directly assiciated with the names themselves or whether all attributes
of names are allowed to blow with the wind.  The latter choice leads
to unreliable programs.

|> [...]
|>    The word `type' has no meaning at all if all attributes are
|>    dynamically alterable.  In this case you _may_ decide to apply the
|>    word `type' to certain properties of _values_, but such a
|>    distinction is not useful.
|> 
|> Why is this distinction not useful?

I have no use for the distinction.  Please post any specific example 
which you feel contradicts my opinion.  If you can't then it's clear
that you don't have a use for the distinction either.

|> [...]
|> For example,
|>      f"1 x
|> means that f applies to the vectors in x, and any additional
|> dimensions are processed "in parallel" [with a unique instance of f
|> applied to each vector].

Aha.  So there _isn't_ a way for the function to be restricted to 
apply only to one-d arrays.  Here you allow x to be of any rank and
apply the function in parallel to those ranks above one.  What I have
consistently asked for is a way to declare f so that it statically
restricts it's arguments to specific constraints - like rank.

What I want is a way to state right up front that "every occurrence
of x in this program is a one-d array" and that "every occurrence of
f in this program is a function which takes one-d arguments" and have
the compiler check every occurrence of these two identifiers to make
sure they obey these constraints.

|> [...]
|>    Terseness in and of itself does not achieve clarity, and can hinder
|>    insight into the problem.
|> 
|> A terse language can be used in a verbose fashion.  A verbose language
|> can not be used in a terse fashion.  Beyond that, insight is required
|> to express a concept in a concise fashion.

Any language which does not contain mechanisms to declare desired
constraints cannot do so.  Period.  No matter how verbose you want
to write your code, the contraints cannot be expressed.

|> [... J is terse and illegible ...]
|> 
|> What sort of metric are you using for legibility?  One that reflects
|> length of code, or one that reflects the underlying semantics?

Both.  A _very_ short coding implies that many semantically complex
concepts are represented non-mnemonically and with no redundancy.
Redundancy is the friend of both correctness and legibility.  Having
too much redundancy is tedious, having none is dangerous.

|> [...]
|>    I've seen APL experts spend 30-40 minutes studying a _single_ line
|>    of APL before they thought they understood what it did - in fact,
|>    this was common.
|> 
|> Well, it's pretty simple to trace through an operation to see how it
|> works.  I don't think I've spent that long studying a single line
|> since the year I learned the language.

Well, in APL it was _VERY_ common.  In fact, I remember 80 character
expressions which were entire lexical scanners and other complicated
things.

|> [...]
|> And, once you've recognized an expression, you don't need to repeat
|> the effort to recognize it the next time around.

As long as the `next time around' is in the next ten minutes.  Six
months later it will be just like seeing the thing for the first
time all over again.

|> [...]
|> Finally, that single line might accomplish the equivalent of several
|> pages of code in some other language.  If so, your experts might have
|> taken even longer to figure out what was happening in this other
|> language.

No, most of them quickly admitted that the same program in Algol 
would have been fairly easy to grasp immediately.  Saving keystrokes
is not an issue, saving programmer time is.

|> [...]
|>    The other aspect of this question is the degree of specificity that
|>    the language requires/allows.  Here the _user_ must be allowed to
|>    decide what degree of specification is needed.  Conventional
|>    Fortran _requires_ all arrays to be of known base type and fixed
|>    rank.
|> 
|> I thought you just said that the user must be allowed to make the
|> decision about whether this is needed?

_I_ did.  _Fortran_ makes a requirement that _I_ think the user
should be allowed to override.  That's why I'm making a proposal for
extensions.  However, Fortran's iron rule is better for reliability
than J's lack of any discipline at all.

|> [...]
|>    To be sure, Fortran requires you to be overspecific.  It seems to
|>    me that J doesn't allow adequate specificity.  Further, there is a
|>    balance point at which most programs exist.  Additional specificity
|>    beyond this balance point should require explicit declaration.
|>    LESS specificity than the balance should _ALSO_ require explicit
|>    declaration.
|> 
|> Could be.
|> 
|> But I think it's easier to add special cases to a language than it is
|> to add generality to a language.

Actually, it's the other way around.  In any case, you're advocating 
_NOT_ adding special cases to J.  It is my contention that J needs them
desperately.

As an example of generality being easy to provide, Fortran 90
will allow a function to be declared (actually, overloaded) with 
the following calling sequence:

      REDUCE (FUN, ARRAY, DIM, DIR)

where FUN is a diadic function defined on the base type of ARRAY
which returns results of the same type; ARRAY is an array of arbitrary
rank; DIR is an optional flag specifying whether the reduction is to be 
done left-to-right, right-to-left or in any order the compiler pleases 
(it's an assiciativity flag); and DIM is an optional argument which
gives the axis along which reduction is to take place.  This makes 
REDUCE a completely general reduction `adverb' in the sense that you
recommend (in fact, due to the DIR flag, this is _MORE_ general).

Although our proposed extensions to Fortran 90 would make this easier 
to define, Fortran 90 _can_ define such a function without any extensions
(at least for all Fortran's built-in data types, one advantage of our
extension is that we could make it generic in user defined types as
well).

A similar function can be defined for PSCAN (Fortran already uses
the name SCAN for something else) which could be written to perform
the same `adverb' that `\' does in APL and J.  The exception would
be that the associativity flag would be limited to left-to-right
or right-to-left (scanning doesn't make much sense as a completely
associative process - partial results appearing in any order?).

|> [...]
|> Remember, identifiers in J have no more significance than braces or
|> parenthesis do in other languages.  [...]

A property of the language I find _very_ bad and I can't imagine
why you think it useful.

|> [...]                            The only real difference between
|> an explicit identifier and some intermediate result is that of
|> persistence.

There is another difference (more important in my view): the type of
the identifier cannot be locally and statically determined like the
type of an expression (all of whose operand types are known).  This
means that the lack of knowledge cascades.  If you can't determine 
the type of an identifier at the site of its use, then you can't
determine the type of the expression it appears in, and you can't 
determine the type of another identifier you assign the expression to,
etc., etc.....  By the time a type error actually generates a visible
symptom the actual cause of the error may be lost in the mists of the 
past.

To be sure it is _sometimes_ desireable for the attributes normally
used as types to be allowed to vary instead.  In this case, you should
have to say explicitly that you're doing so.  Even then, it's useful
to be able to restrict the attributes in some way - you might want X
to be allowed to be integer, floating-point, or double precision but
never complex or character, for example.

|> [...]
|> Compilation is the act of partial evaluation, to take advantage of
|> constant results and invariants (which are often, in essence,
|> constants).

The more of that `partial evaluation' that can be carried out at
compile time, the better.  Your code will be faster.  If the language
constructs which make this possible are mnemonic, your code will also
be easier to read, debug, and maintain.

J. Giles
Newsgroups: comp.lang.misc