From: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell)
Subject: Re: Types, arrays, generality, and notation (was: Fortran90 and Arrays)
Organization: University of Maryland University College
	<ROCKWELL.92Apr8164502@socrates.umd.edu>
	<1992Apr9.003132.5175@newshost.lanl.gov>
Date: Thu, 9 Apr 1992 04:22:40 GMT

Jim Giles:
   |>    My example didn't copy _any_ elements `in place'.  The
   |>    destination locations were completely disjoint from the
   |>    source locations.

Raul Deluth Miller-Rockwell:
   |> What does completely disjoint mean?  It's my understanding that
   |> you were modifying storage in the very array you used as an
   |> argument.

Jim Giles:
         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.

It also illustrates a big difference between assignment in J and
FORTRAN.  In J, assignment affects the identifier.  In FORTRAN,
assignment affects the underlying object.

   Your J version of this operation was _very_ clumsy:
         A =: 2 # ((#A) $ 0 1) # A

On the other hand, the J version is, in essence, side effect free.
All instances of A-after-assignment could be replaced by the
expression-involving-A-before-assignment.  Whether this allows the
compiler to utilize re-write rules for this specific case is not
something I've spent any time on.

   How would this have been different if I had used the example:
         A(0: :2) = B(1: :2)

I would have had to write a completely different expression which
expressed the merging of A and B.  Perhaps I should have written such
an expression for the expression in A, above?  [by the way, what's the
blank space for, in A(0: :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.

Yes, A must appear on the right hand side of the assignment.  Whether
or not that means that A's storage can not be reused depends on
whether or not the prior value of A must be referenced after this
assignment.  [Remember, in J, the identifier is not the object.]

   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.

I've not seen this problem occur with J.

And, no, I've not written any massive code in J, but I do maintain
some fairly massive code in APL -- which has quite similar properties.

   |>    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.

Sure, if I've an expression of the form:
   A + B

then both A and B must have some sort of numeric type.

   |>      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.

Eh?  But f's arguments are restricted in rank.

Maybe you're complaining that you want 'x' to be restricted so that it
can only be a vector?  If so, try

    f @only_vector:  x

where:
   only_vector:  =. (invalid ^: not_vector) f.
   invalid       =.  '' : ''
   not_vector    =.  1 & ~:  @rank
   rank          =.  # @$

You'll probably argue that this definition is inelegant.  However,
I'll assert that the 'inelegance' is a restatement of your
unfamiliarity with the notation.

   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.

Well, if you're not going to change f, you could use

f:   =. f  @ only_vector:       fix

where
fix =. f.

(f. is an adverb of the language, which has semantics similar to those
you'd expect in a compiler.  Essentially, it strips all dependence on
identifiers from the definition of a verb.  I used the cover-name
'fix' to distinguish 'f.' from the user defined names 'f' and 'f:').

   |>    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.

So?

At worse, the expression should be broken up into several lines, and
perhaps meaningful identifiers should be used.  Besides, if the
lexical scanner is simple I don't see why it should require a lot of
code.

   |> 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.

So far, you've only asserted that there is difficulty for someone who
knows the language.  Besides, if it were really obscure you could
always comment the code.

   |> 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.

Hmm.. I don't think your experts knew APL very well.

   _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.

All I can say is you've not programmed in J to make a statement like
this.  J can be pretty picky about error conditions.

   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.

I've not noticed the need.

   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).

Overloaded?  How so?

Also, this looks more like a conjunction (which derives a function
from several pieces of information) than an adverb (which derives a
variant of the function without further information).

Note that the user of J can define a REDUCE word with exactly these
semantics.  Though, not the same syntax -- a function in J, be it
declared via an adverb or conjunction or other technique, always
follows the same syntactic rules.

   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).

This does sound like an improvement.

   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?).

Actually, the APL adverb '\' is different from the J adverb '\'.  To
get the effect of APL's '\' in J, you need to use '/\'.  In J, the
scan operation is viewed as a partitioning operation, and there's a
variant which allows for successive suffixes instead of successive
prefixes.  What you do with these sequences is up to you.

   |> 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.

I find it conceptually simpler than systems where I must pre-declare
assignments.  If, as is usually the case, I do not re-declare any
names, the entire definition for the name is in one spot, which means
I don't have to go hunting around through source code to find the rest
of the definition.

   |> 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).

The type of an identifier can be determined from the expression used
to declare it.  All expressions are ultimately rooted in constants of
some sort, or input operations.

   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.

In which case, the user can define a word which requires this
condition and use it wherever appropriate.

-- 
Raul Deluth Miller-Rockwell                   <rockwell@socrates.umd.edu>
Newsgroups: comp.lang.misc