Article View: comp.lang.misc
Article #119Re: Types, arrays, generality, and notation (was: Fortran90 and Arrays)
From: Raul Deluth Miller-Rockwell
Date: Thu, 09 Apr 1992 04:22
Date: Thu, 09 Apr 1992 04:22
271 lines
12626 bytes
12626 bytes
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>
Technical Details
Message-ID:
<1992Apr9.042240.26041@socrates.umd.edu>Path:
arc1-ca.pugleaf.net!sparky!uunet!haven.umd.edu!socrates!rockwellReferences:
<ROCKWELL.92Mar21001252@socrates.umd.edu> <ROCKWELL.92Apr8164502@socrates.umd.edu> <1992Apr9.003132.5175@newshost.lanl.gov>Show Full Headers
Complete Article Headers
Newsgroups: comp.lang.misc Path: arc1-ca.pugleaf.net!sparky!uunet!haven.umd.edu!socrates!rockwell From: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell) Subject: Re: Types, arrays, generality, and notation (was: Fortran90 and Arrays) In-Reply-To: jlg@cochiti.lanl.gov's message of 9 Apr 92 00:31:32 GMT Message-ID: <1992Apr9.042240.26041@socrates.umd.edu> Organization: University of Maryland University College References: <ROCKWELL.92Mar21001252@socrates.umd.edu> <ROCKWELL.92Apr8164502@socrates.umd.edu> <1992Apr9.003132.5175@newshost.lanl.gov> Date: Thu, 9 Apr 1992 04:22:40 GMT