[comp.lang.apl] J?

feustel@netcom.COM (David Feustel) (03/17/91)

I'm new. What's J? Is source available? What machines and OS's does it
run under? Thanks.
-- 
David Feustel, 1930 Curdes Ave, Fort Wayne, IN 46805, (219) 482-9631
EMAIL: netcom.com

cs450a03@uc780.umd.edu (03/17/91)

David Feustel:
>I'm new. What's J? Is source available? What machines and OS's does it
>run under? Thanks.

Hi, 

This may be restating the obvious, but J is a programming language.
It is, I think, more than a little elegant.  The current incarnation
is probably what you'd call a beta-version.  It's not quite fully
implemented, and could stand some real speed improvements.  (It is,
however, infinitely faster than vaporware.)

Source, sadly, is not available.  Though if the GNU people get rich
enough the Iverson Software people may follow in their footsteps.
(Iverson Software Inc. is the group writing/providing the
interpreter.)

As for what machines it runs on, memory tells me pcs, sun4s,
bsd-vaxen, mips machines, and I believe next machines and macs, and
probably others.  Ftp 129.97.129.140 (I think that's
watserv1.waterloo.edu), /languages/apl/j ... subdirectory by
architecture, for binaries.  If other machines than these are
supported, I'm sure the ISI people will be happy to tell you.

Slight problem... there isn't much in the way of on-line docs for J.
I'm not sure how such docs fit in with ISIs philosophy...  Currently,
if you send them $24, they'll send you docs and a copy of their latest
version of the software...  If you can tolerate dense information, the
binaries come with a complete list of commands (indicating what's
implemented), and detailed docs on each of the system commands.  There
are also about 40 screens worth of example code, grouped by subject.
There's also a little custom previewer to look at the example code.

Following is a quicky example (something I was playing with earlier
today).  I didn't write it for anyone else, so it has only one
(useless) comment, but I'll try and add some docs at the end. 

Example (uuencode) follows...


$ j
J Version 2.9   Copyright (c) 1990 1991, Iverson Software Inc.

   2!:4 <'uu.jws'
1
   uul
+---------+--+----------------------------------------------------+
|(#y.)$:y.|::|uunum =. #. ((4 * >. x. % 3),6) $, (8#2) #: a. i. y.|
|         |  |   a. {~ (32 + x. , uunum), 10                      |
+---------+--+----------------------------------------------------+
   uul 'foobar'
&9F]O8F%R

   uuenc
+--------------+--+-------------------------------------------------+
|'644 file'$:y.|::|nl=.    10 { a.                                  |
|              |  |'mn'=.  0 45 #: # y.                             |
|              |  |begin=. nl,'begin ',x.,nl                        |
|              |  |end=.   (uul ''),'end',nl   }: 'requires uul'    |
|              |  |   begin, (,uul"1 (m,45)$y.), (uul (-n){.y.), end|
+--------------+--+-------------------------------------------------+
   uuenc 'This is a test, This is only a test.  If it weren''t a test.....'

begin 644 file
M5&AI<R!I<R!A('1E<W0L(%1H:7,@:7,@;VYL>2!A('1E<W0N("!)9B!I="!W
297)E;B=T(&$@=&5S="XN+BXN

end



(Incidentally, I hope you have a way of splitting your screen to read
this, otherwise I guess you'd have to print it out...)

First, programs name is 'j', you get that banner every time you start,
and the default prompt is three spaces.

Second, that 2!:4 is a raw system call.  Library 2 is workspaces (J
will allow you to manage objects on file by name, if you'd rather that
than raw character data).  Call 4 happens to be a read command.  The
file I wanted to load was called uu.jws, and since the call needs a
pointer to a string, < makes one (kind of like * in c).  The 1 is a
return code indicating success.

Most people consider this sort of thing gory, and make themselves a
profile file to provide nice covers for system calls and such.  I
don't bother, because I've been using J on a guest account, and
everything gets blown away every few days.

The routine 'uul' takes a line of up to 45 characters and returns the
corresponding line of uuencoded garbage.  Quick summary:

        ::  says this is a verb (function).  The string on the left is
the verb's intransitive form (takes only one argument).  The string on
the right is the verb's transitive form (takes two arguments).

        $: is a self reference (by the verb, to itself).  Normally,
it's used for recursion, here I was playing around with the idea
uuencode, and thought I might want to claim that a string had some
length different from what it really had.  So the transitive form
takes a left argument of the strings 'intended' length.

        y. is the right argument to the verb.  In the transitive case,
x. is the left argument.

        # counts how many elements are in a list (or string) when used
intransitively, and if used transitively the left argument says how
many copies to make.

	a.i.y. converts an ascii string into numeric form.  (a. is the
ascii character set, with ascii character 0 in position 0, ascii
character 1 in position 1, etc.)  i. looks up each character and
returns the indices (which are the ascii codes).

	#: converts from number to a list of digits which represent
that number.  The eight 2's mean I get 8 bits (base 2 digits) for each
number.

	The , (used intransitively) means ignore the structure of the
array that #: generated.  This is important because $ (used
transitively) gives it a new shape:  Instead of 8 columns, it will now
have 6, and to make up for than, I increase the number of rows by a
third.

	% is division, so x. % 3 is one third the original length.  >.
raises that value to the next highest integer (if it's fractional).  
4 * ... multiplies by four.

	#. converts from the base two representation back to a list of
numbers.  These numbers each will be encoded as characters on the next
line.

	=. is an assignment statement.  It localizes the name on the
left too.

        On the next line, (,) is used transitively to catenate the
number of characters represented onto the beginning of the line.  Then
all these numbers are offset by 32, to avoid control characters.  A 10
is added onto the end (ascii 10 is newline).  Then the numbers are
converted to ascii.

	a.{~y  is roughly equivalent to the C statement a[y], except
that it works on all the data you specify.  Thus, a.{~ 102 111 111
would return the three character string 'foo'.  (By the way, ~ means
swap the arguments (give the right argument on the left, and the left
argument on the right)  102 111 111 { a.  would also yield 'foo'.)

	The last line executed in a verb gives that verb's return
value.  It is unfortunate, I suppose, that you can't tell from the
display that the intransitive form of uul has only a single line.

	Still with me?  I suppose it should be apparent by now that a
single line in J will often hold as much meaning as a full screen of
text in a number of other languages.  It would be a mistake to try and
read over J by simply glancing at a page and expecting to get the gist
of what's going on in a couple seconds.  Or, not at first at least.

	If you can get used to reading one line as carefully as you
would a number of lines in lisp, or modula, or whatever, you should
find that it is just as understandable.  Of course, it's alway handy
to have a computer at hand to see what each part of a statement does
to representative data.

	I tried a short example of uul, not to attempt to show how it
functions (to do that, you'd want to do something a bit more
step-by-step), but to compare with the results of uuencode.  The
garbage looked the same, so I wrote a cover to handle the larger parts
of the uuencode format.

	The intransitive form of uuenc is very similar to the
intransitive form of uul:  In this case I'm providing a default header
(file access privileges, filename) argument so I don't have to worry
about that for testing.

	First, since I was using more than one instance of the newline
character, I stuffed that into a variable.

	Second, since uuencode puts out 45 characters per line (except
for the last line), I convert the number of characters in the argument
string to a two digit 'base 45' number.  (Well, sort of.. that leading
0 means the leading digit can be as large as necessary to hold the
result, it is not cut off arbitrarily at 44).  The string 'mn' that I
assign into means variable m gets the first number (the number of
whole lines to be converted), and the variable n gets the last number
(the length of the last, partial length line).  J uses two quotes when
quoting variables, instead of a single quote (like lisp does), and
allows a shortcut (which I use here) when using variables with single
character names.  If I wanted to be really formal, I would have
written:     ('m';'n') =.  0 45 #: # y.    Or, I could have gone
further and computed the quotient on one line, and the remainder on
another.

	Incidentally, it just occurred to me to wonder if uudecode
will choke if there are two terminating lines (nl,' ',nl,' ') between
the last line of garbage and the 'end' string.  I don't think it will,
but feel free to test for that condition.

	Ok, the third line of uuenc constructs the header.  Nothing
really but simple string catenation.

	The fourth line constructs the ending sequence.  uul applied
to an empty string simply returns a space and a newline, but I was
feeling a touch formal when I wrote this.  If it turns out that
uudecode chokes with two blank lines at then end, I'd throw a test in
here to get rid of the space,newline when it isn't needed.

	Finally, I put all the pieces together and return that as the
function result.  Starting in the middle,  (m,45)$y.  converts the
argument string into a table of characters where each row is 45
characters long.  Usually this table is slightly smaller than the
original string and a few characters get dropped off the end.  

	uul"1 means that I am passing to uul individual strings (i.e.
one dimensional arrays).  J automatically executes uul once for every
row, and puts together the result into a single array.  After that, I
use the intransitive (,) (called ravel) to throw away all the details
of the array.  After all, my result is intended to be just a string.

	(-n) {. y.   grabs whatever would get left out of that big
array and runs it through uul separately.  I think this is important,
because the manual pages on uuencode didn't seem to permit a lot of
padding at the end, at least not before the end marker.

	To test this out, I ran a few strings through uuenc, stuffed
them out to file, and then ran uudecode on them.  The basic concept
looks good (no bit-reversal problems or anything like that).

	I think this is an order of magnitude slower than uuencode,
but it is at least somewhat educational.

	And, who knows, maybe with future versions of the interpreter
speed for things like this will increase drastically.  (Not entirely
an unreasonable idea.  I found that on a 16Mhz 386, with a version of
J from last October, I was getting about 2 milliseconds per integer
addition (that's trying things like  x + y  where x and y each had a
thousand integers in them).  Like I said earlier, quite a bit faster
than vaporware.  But still well below what I expect the performance
ceiling to be.

	uuenc, on a rather loaded sun4, generally took what seemed
like a couple seconds to run through a 5 lines message.  Of course I
didn't actually time it, and I was experiencing some pretty heavy-duty
transmission lags (around a second or two between hit return and see
response for just about everything), but that's internet for you.


Raul Rockwell
P.S.  This has gotten long, sorry about that.

cs450a03@uc780.umd.edu (03/17/91)

I just re-read my last post.  It has an embarrasingly large number of
typos (I knew I should've proofread it before I posted...).

If the language is too muddy for anyone, write me.  I'll email a
clarification.  If I get many queries, I'll post too.

Raul