Article View: comp.lang.misc
Article #135Re: Types, arrays, generality, and notation (was: Fortran90 and Arrays)
From: Raul Deluth Miller-Rockwell
Date: Fri, 10 Apr 1992 06:11
Date: Fri, 10 Apr 1992 06:11
309 lines
14736 bytes
14736 bytes
Jim Giles:
Place finger in mouth, pull it out rapidly making POP sound, point
finger straight up and wave in circular motion, say "woopeee".
If the natural way to think about what you're doing involves
side-effects, use the damn side-effects! This notion of removing
side-effects from a language just for the esthetic "purity" of it
has no appeal to me at all.
Yeah, and?
Your "natural side effects" are available. They're just not tied to
identifiers, because they're expressed as function results.
The point being that it's likely that, whatever real-world problem is
being solved, there are other ways of expressing that same problem.
[Its generally true that a literal translation of code, from language
A to language B, does not exhibit the original algorithm as nicely as
if the code had been written directly in language B. The issue of
side effects is a symptom of this problem.]
|> How would this have been different if I had used the example:
|> A(0: :2) = B(1: :2)
Now, would you care to actually answer the question I implied
above? How does J represent the assignment "A(0: :2) = B(1: :2)"?
[By the way, thank you for repeating the definition of a triple.]
In J terms, this is four operations:
[1] selecting those items in B which are to be merged with A
[2] selecting the locations in A at which to merge those items
[3] performing the merge
[4] calling that result A
Now, since J doesn't provide this notation for triples, I'll have to
provide that utility myself. For now, let's say that I have a
routine:
array step n
that produces the indices
0, n, 2n, ... such that all indices are valid indices of array
Then, I would write part 1 as
B {~ 1 + B step 2
part 2 would be written
A step 2
Combining these with the merge operation
(B {~ 1 + B step 2) (A step 2) } A
And, finally, putting that result in A
A =. (B {~ 1 + B step 2) (A step 2) } A
If I was doing a lot of this, I might define some more general verb to
capture the essence of the more general operation. As it is, I'm
using J to emulate Fortran.
Finally, a definition for step:
step =. 'where: x. $ y. {. 1' :11
where: =. 'y. # i. # y.' :11
Basically, 'where:' is a verb which converts from a bit vector
representation to an index representation (I use this one quite
frequently). The rest of the definition of 'step' implements a
stride specification.
By the way, ':11' is a sort of a J compiler. [Compiles simple
expressions to an internal representation of a verb.] For this
mini-compiler, x. represents the left argument of the resultant verb,
and y. represents the right argument of the resultant verb.
Also, note that although I've not made any explicit declarations about
the sizes of A and B, there are a number of such assertions implicit
in the above code. The above code will work unless B has an even
number of elements, nor will it work unless A step 2 is identical to B
step 2.
This is the best I can think of without further insight into the
problem you're trying to solve.
The identifier is not _the_ object in _any_ language.
Well, I wouldn't go that far. Consider LISP, for example.
The identifier is _a_ syntactic object which _denotes_ something in
the semantic domain(s) of the language. In this case, the
identifier denotes an aggregate, the size, shape, and base type of
the aggregate are semantic properties that the identifier denotes -
as are the specific values within the aggregate.
What you appear to be saying is that in J _all_ these properties
must be respecified when assigning - you can't independently alter
the attributes denoted by the identifier (like ,say, _some_ of the
values in the array). To simulate this, you may reference the
prior definition of the identifier as part of the new description
of it, but you must, somehow, fully respecify all the attributes at
every assignment.
Yes. It gets worse: J doesn't allow you to change a single element of
an array without respecifying the entire array.
This means that the popular coding style of modifying an array one bit
at a time is tremendously inefficient in J. APL shares this
characteristic, though to a lesser degree. There are times, of
course, where this gets in the way of fast code -- traditionally, in
APL, this has been addressed by providing a compiler which specializes
in converting this coding style to something efficient.
This characteristic of J means that I can't enhance legibility and
reliability of the program by statically specifying _some_ of the
attributes I want associated with a given name and having the
compiler statically enforce my desired constraints.
What I like to do is build up a set of verbs which express the basic
operations needed by my problem domain. Then I use those to express
the solution to the problem. This is somewhat akward to do in some of
the other languages I know -- because the languages lack the ability
to express generic operations on arrays.
|> [... distinction between `type' and `value' in a typeless language ...]
|> Sure, if I've an expression of the form:
|> A + B
|> then both A and B must have some sort of numeric type.
This can be _statically_ determined? That is, the compiler can
_always_ tell at compile time that both A and B obey this
constraint? (Here I'm assuming that `+' is _never_ overloaded for
non-numeric data.)
First off, you are correct. '+' is never overloaded for non-numeric
data. So, yes, the compiler can always tell at compile time that both
A and B obey this constraint.
Of course, what I've not mentioned is that there is nothing in the
above expression to determine whether A and B represent numeric _data_
or functions returning numbers. But if that information is not
available to the compiler then A and B must be undefined, and the
compiler is justified in refusing to compile this expression.
Note that there is no need for any compiler to compile all J programs
-- a compiler in J is just another meta-function, with a specific
domain and range. For example, a compiler whose purpose is to provide
speed for highly serial code need not to support operations better
left to the interpreter.
|> [... doing static type checking on f x ...]
|> 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
You mean I have to explicitly say this in the code at the site of
_USE_ of the function or variable?
You can define an identifier to represent f @only_vector:
Bletch. Also, it looks to me that this would usually be a run-time
test unless I made such a constraint at _every_ assignment to x and
the compiler can actually determine that I had done so.
A compiler can be made to propagate such constraints backwards to the
point of function invocation. Run time checks are only bad if they
get into inner loops.
|> [...]
As far as I can decipher, this does less than _half_ of what I
asked for. It defines f in such a way that it carries its run-time
test on its argument wherever it goes. It does not make the test
work at compile time and it does not prohibit assignments to x
which violate the constraint I wanted to put on it.
If assignments to x would result in the constraint being violated then
those assignments can be prohibited. This depends, of course, on the
design of the compiler. And, of course, on the topology of the
constraint.
|> 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.
I used to know APL fairly well (in the seventies). I always had
trouble understanding it. Especially other people's code. Moreso
than I have trouble understanding other people's C code (which is
also hard because of myriad non-mnemonic operators, way too many
precedence levels, and too densely packed expression notation). I
have less trouble undersatnding other people's Fortran code and
even less other people's Pascal code. Pascal has numerous faults,
but its legibility is first rate.
Hmm... I find it easier to read APL than Fortran. I find it easier to
read than Pascal too. It _feels_ to me like this is because APL code
gets straight to the point, while these other languages futz around
dealing with almost (but not quite) irrelevant issues. But, to be
fair, it may be because I work with APL and not these other languages.
|> Hmm.. I don't think your experts knew APL very well.
I worked at the help desk at my university. I interacted with most
of the APL users in the whole school as well as the _implementors_
of the local APL processor (I had to - if a problem arose which I
could not answer). All agreed that its syntax was opaque.
A number of old APL implementations had "wonderful" features such as
eating all redundant white space from user code. If your implementors
were doing this sort of thing then I can see why they might make that
sort of statement. Also, early APL was much harder to generalize with
than modern APL.
But I understand it still did pretty well on some scientific and
numeric problems...
|> [...]
|> Note that the user of J can define a REDUCE word with exactly these
|> semantics. [...]
Oh? I thought you said originally that J couldn't specify whether
something was to be reduced left-to-right, right-to-left, or in any
order. Whether the function FUN is to be regarded as left- or
right-associative or just associative makes a considerable
difference numerically as well as (in some cases) making a
difference in speed.
I don't believe I said that. I may have said something similar about
the properties of a specific word in J.
I recently (about 2-3 weeks ago) posted to comp.lang.apl a technique
for any-order reduction for use when the order of reduction affects
execution time. The particular solution was for a dot-product
reduction of a list of arrays (of different but conforming sizes), but
the technique could be generalized as a meta-function which combines
an arbitrary verb with an arbitrary rating method.
You mean _one_ of the entire definitions of the name is in one
spot. If you're working on a large code in a team, of if you're
importing code from another site to be integrated into your own
code (like getting a professionally written graphics package) this
is dangerous. Suppose both you and the other programmer define a
global called X. Very probably the two definitions are very
different. Yet the compiler will accept _both_ definitions and, at
any point in the program, will use whichever was defined most
recently. With conventional languages, the type mismatch could be
found before run-time (though whether at compile or load time
depends on how you integrate components).
A feature has been recently specified for J (though not yet
implemented) to provide name resolution for these cases. Basically,
the name X is prefixed with the package name.
There are further problems. Suppose you define FUN2 as a function
which is defined terms of FUN1 - another function. Subsequently,
you redefine FUN1 (you claim that this is something you like to do
while developing code). But now FUN2 is still defined in terms of
an obsolete version of FUN1 (if I understand your rules correctly).
This means that every time you redefine one thing, you must
redefine _all_ the things that depend upon it!
Actually, no. Depending on how I've defined FUN2, it will
automatically acquire the new definition of FUN1. Besides, when I'm
doing this sort of thing, I usually build (at the same time) a script
which would instantiate the current set of definitions -- if I need to
change something that I've made "permanently constant" I can always
step outside the language and begin again after executing this script.
|> The type of an identifier can be determined from the expression
|> used to declare it. [...]
Which is _not_ locally visible at the site of the use of the
identifier, nor is it in some cannonical place (like the beginning
of the present module or procedure). It can be _anywhere_!
Well, in principle, yes. In practice, no.
Note that most APL code (and most J code) is "straight through" no
loops, no side trips to the bahamas, nothing... just top to bottom,
straight through.
There will be some sort of outer structure (where the user interacts)
where the order of execution is uncertaion. But if something is
undetermined beneath that level then you've got a programming error.
And it's pretty easy to write tools which support this coding style.
Exactly. The `word' I want to define is the identifier itself.
This way I can't forget to use the limitation `word' when I use the
identifier. The way you appear to be suggesting is like that thing
you mentioned before:
f @only_vector: x
Which only applies the limitation when you remember to explicitly
state the `@only_vector' part on every use of f.
F =. f @only_vector:
should do what you're asking for.
And, so far as you've presented, there is no way to make such
limitations on assignments. For example, how do I define X or some
`word' associated with X that prohibits X from being assigned
anything but integers?
set_X =. 'X =: invalid: ^: not_integer: y.' : ''
You might like to think of ^: as the word 'IF'...
--
Raul Deluth Miller-Rockwell <rockwell@socrates.umd.edu>
Technical Details
Message-ID:
<ROCKWELL.92Apr10011141@socrates.umd.edu>Path:
arc1-ca.pugleaf.net!sparky!uunet!haven.umd.edu!socrates!socrates!rockwellReferences:
<ROCKWELL.92Mar21001252@socrates.umd.edu> <1992Apr9.042240.26041@socrates.umd.edu> <1992Apr9.192421.1996@newshost.lanl.gov>Show Full Headers
Complete Article Headers
Newsgroups: comp.lang.misc Path: arc1-ca.pugleaf.net!sparky!uunet!haven.umd.edu!socrates!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 Thu, 9 Apr 1992 19:24:21 GMT Message-ID: <ROCKWELL.92Apr10011141@socrates.umd.edu> Sender: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell) Organization: Traveller References: <ROCKWELL.92Mar21001252@socrates.umd.edu> <1992Apr9.042240.26041@socrates.umd.edu> <1992Apr9.192421.1996@newshost.lanl.gov> Date: Fri, 10 Apr 1992 06:11:41 GMT