From: stephen@estragon.uchicago.edu (Stephen P Spackman)
Newsgroups: comp.lang.misc
Subject: Re: Fortran90 and Arrays
Date: 7 Apr 92 01:31:29 GMT
	<ROCKWELL.92Apr4183720@socrates.umd.edu>
	<1992Apr6.192714.4670@newshost.lanl.gov>
Organization: University of Chicago CILS

In article <1992Apr6.192714.4670@newshost.lanl.gov> jlg@cochiti.lanl.gov (Jim Giles) writes:
|In article <ROCKWELL.92Apr4183720@socrates.umd.edu>, rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell) writes:
||> [...]
||> By "maintenance cost" I meant a cost in maintaining the code -- time
||> required to analyze what's going on if an error is found and/or if an
||> enhancement is needed.
|
|In which case, our extension already seems far superior to J.

But does that make it right?

|[...]
|The first requires usderstanding only the rules subscript triples.
|The second requires understanding several different operations - most
|of which you _don't_want_ the compiler to actually do because what
|you want done is operationally most similar to the Fortran 90 version.

But most of what you write in a FORTRAN programme you don't want the
compiler to do - you ALWAYS want competent compilation. Not, of
course, that I'm claiming that the example I elided bore in favour of
J in this case.

||> [... operators with precedence rules ...]
||> I don't think you've thought through the implications this will have
||> on meta-functional forms.
||> 
||> If nothing else, it'll be another set of arbitrary rules to memorize.
|
|Untrue.  The rules of J are apparently that there is no precedence.
|That will be true in Fortran 90 for all operators which are user defined.
|So, for Fortran 90 there are no more rules to learn beyond what J requires
|except for operators that already follow the `natural' precedence that
|users actually desire.  These rules are certainly not arbitrary, but 
|reflect the way people acually denote arithmetic operations even when
|not programming.
|
|Further, I don't think that it's much to a user's advantage to define
|a lot of operators.  The function call syntax is better suited to those
|operations which do not fit into the `natural' precedence scheme.

Don't yopu detect the circularity in your own reasoning here? You are
opposed to user-defined operator syntax because it cannot have the
natural precedence for the language, but the reason it doesn't have
the natural precedence for the language is that it was specified that
way. Justified, of course, by the fact you don't really want to use
it....

Designing in braindeath is not the thing to do.

Your position CAN be justified, along these lines: Ok, centuries ago,
we screwed up. We wireed in precedence and it louses up user-defined
operators, and heaven opnly knows how we didn't see it coming. But the
mistake has been made, we can't trash all that extant code, and
operator priority declarations have been tried and they're a
nightmare.

One alternative that I rather like (but that is also hard to retrofit)
is to introduce operators at different precedence levels, but whose
precedence can be determined from the opertor spelling; so perhaps all
things of the form x: (for some opsym x) bind looser than those of
form xx, which bind looser than single-character operators....

||> [...]
||>    I don't see the how this is true.  If the rank of the array is
||>    unknown, what operations can a recursive algorithm even apply to
||>    it?
||> 
||> addition, subtraction, multiplication, division, count atoms, count
||> dimensions, count items, various reductions, transposition, and
||> various forms of selection come to mind.
|
|Some of these cannot be done on arrays of arbitrary rank.  Transposition,
|for example, makes no sense for arrays of rank less than two.  So, the use
|of these operations requires _some_ static knowledge of the rank of the
|arguments.  This static knowledge should be encoded explicitly into
|the function definition so that the optimizer has a chance of producing
|good code. 

You forget that the optimiser is ALREADY looking at stride patterns
and not at the source description. In the VERY worst case you can get
excellent performance by passing optimised access and iteration code
in as part of the array descriptor. There's nothing inherently
inefficient about polymorphism, because the storage layout of an
object is always known SOMEWHERE. And we're talking about an
incredibly mild flavour of polymorphism here....

In any event, how does adding a general case impair performance for
the already-identified common case? Are you really planning to write
compilers THAT DUMB?

|For example, you claim that the `catenate' operator can append a 
|length 3 vector to a 3x3 array and produce a 4x3 array.  I seems
|to me that the intuitive way for this to work should produce a 3x4 
|result instead.  Seeing that this difference in intuition is not
|reconcilable, I would would be willing to compromise by not having
|_any_ default ordering.

But there's nothing "intuitive" about the english language either. And
we do ok with a FAR less well-specified system. I have to agree that
manic consistency will get you a long way (look at it this way: out of
FORTRAN and J, which one are you going to have better luck learning in
two hours? My bet goes with J *even though* it is cryptic and - not
counter-intuitive - counter-historical).

||> [...]
||> where A had the declarations:
||>    |> >      A(i, j, k, l)
||>    |> >      A(i, j, k, l, w)
||>    |> >      A(i, j, k, l, w, x)
||>    |> >      A(i, j, k, l, w, x, y)
||> 
||> Obviously, any operations which only require that i, j, k, and/or l
||> covary could be meaningfully used. [...]
|
|In which case your arguments to the function are _always_ rank 4.
|It is their base `type' which varies from call to call.  The base
|type of the array is:
|
|	 Type base_type = UNION
|	    real :: R
|	    real :: S(w)
|	    real :: T(w,x)
|	    real :: U(w,x,y)
|	 End base_type
|
|Then the argument to the function is declared as:
|
|	 type(base_type) :: A(i,j,k,l)
|
|Obviously, UNION is our extension.  Unions are always implicitly tagged.
|Any operation which is defined on all the elements of a union is defined
|on the union itself (so, for example, all the above can be elementally
|added to any othe conformable object).  This use of union is simply
|a short-cut for writing procedures which are generic in the types of
|their arguments.

Actually, I think the idea was to change the LOCAL INTERPRETATION of
these arrays and not their FORMAL TYPES at the callpoint. The
introduction of tags, as you suggest, would actually result in
breaking many operations - suppose, for instance, that the final,
7-dimensional array needs to be considered as 4+3 on one line, and
3+3+1 on the next? There are real semantic differences between tagged
and untagged type representations: that's why J has had to add all
kinds of crazy new conventions to get REAL TAGLESS TYPES for cases
like this, in a language that is traditionally tagged in all places!

Both these languages suffer from far too much historical baggage, but
surely there's still room for some POSITIVE dialogue?
----------------------------------------------------------------------
stephen p spackman         Center for Information and Language Studies
stephen@estragon.uchicago.edu                    University of Chicago
----------------------------------------------------------------------