j-stdio.nw Running J in the Unix Environment
Redirections
In the following, the standard library profile has been extended to emit a random quote from the J Phrases collection. This will serve as a simple indication that not just the bare interpreter is loaded.
<session>= bash-2.02$ echo '+/ i. 10' | j J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. m15=: ^@j. NB.r.(Complex # on unit circle at y radians) -- the J Phrases collection +/ i. 10 45 bash-2.02$Input is echoed, J prompts are emitted.
<session>+= bash-2.02$ echo '+/ i. 10' | j -- d9=: <./@i. NB.IO first occurrence in x of any item of y -- the J Phrases collection +/ i. 10 45 bash-2.02$The -- suppresses the copyright message and J prompts. The standard profile is loaded. Input is still echoed to stdout.
<session>+= bash-2.02$ export JLIB=/usr/local/lib/j-402a bash-2.02$ echo '+/ i. 10' | jint-402a J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. +/ i. 10 45 bash-2.02$ echo '+/ i. 10' | jint-402a -- +/ i. 10 45 bash-2.02$Calling the bare interpreter without the wrapper invoking the profile. The location of the JLIB needs to be defined nevertheless, for example to locate the license key.
Again, use of the
--
will suppress the copyright message and the prompts.
Behaviour in case of an error
<session>+= bash-2.02$ echo "'foo', 44" | j J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. d17=: e.~&, # i.@#@] NB.ISO all occurrences of items of y in x -- the J Phrases collection 'foo', 44 |domain error | 'foo' ,44 bash-2.02$
J signals the error and aborts. Even the error message will go to
stdout
, not tostderr
:<session>+= bash-2.02$ echo "'foo', 44" | j -- > /dev/null bash-2.02$
The situation changes a little bit if the commands come from a script, not from
stdin
:
<session>+= bash-2.02$ cat error.ijs 'foo' , 44 bash-2.02$ j error.ijs J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. d23=: +/ .* NB.Scalar (dot) product of vectors x and y -- the J Phrases collection |domain error | 'foo' ,44 |[-0]J becomes interactive and expects further data from
stdin
. If manual intervention is unwanted at this point, make sure thatstdin
is not read from the terminal:
<session>+= bash-2.02$ j error.ijs < /dev/null J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. d13=: 31"_ - 2: | 7: | [ NB.31 - 2 | 7 | x: days in month x, not = 1 -- the J Phrases collection |domain error | 'foo' ,44 |[-0] bash-2.02$
For things like
at
andcron
jobs,stdin
is already well defined, so redefining it is not necessary:
<session>+= bash-2.02$ at + 10 minutes j error.ijs Job 1 will be executed using /bin/sh bash-2.02$ You have mail in /var/mail/neitzel bash-2.02$ mail Mail version 8.1 6/6/93. Type ? for help. "/var/mail/neitzel": 1 message 1 new >N 1 neitzel Sat Jul 17 20:10 18/555 "Output from your job " & Message 1: From neitzel Sat Jul 17 20:10:04 1999 Date: Sat, 17 Jul 1999 20:10:03 -0400 (EDT) From: Atrun Service <neitzel> To: neitzel Subject: Output from your job c0000100ed15c2 m59=: IX=: a.&i. NB.Index in ASCII alphabet -- the J Phrases collection J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. |domain error | 'foo' ,44 |[-0] &
Data via stdin
Just for the sake of completeness:
<session>+= bash-2.02$ j -- << ! > |. (1!:1) 3 > one > two > three > ! d0=: %@>:@(] % 100"_) #. |.@[ NB.Present value flows x at y% -- the J Phrases collection |. (1!:1) 3 eerht owt eno bash-2.02$
If the script is read from input, a read from stdin (as indicated by the
3
) will switch over to take the entire rest of the input as data. You can do this exactly once. The J sentence to trigger the read will be the the last sentence of the script read via stdin. (Of course, it may call previously defined J verbs etc.)If we substitute the stdin code
3
above with the "keyboard" code1
, the input will still be taken from the script source; however, only one single line will be read as data. After that line, input is interpreted again:
<session>+= bash-2.02$ j -- << ! > |. (1!:1) 1 > hello world > +/i.10 > ! Did you know... m23=: 3 : (':'; '{~^:y. x.') NB.Permutation x to the power 2^y -- the J Phrases collection |. (1!:1) 1 hello world dlrow olleh +/i.10 45 bash-2.02$
Typically, one wouldn't combine J commands and data on stdin. stdin really should be used for the data proper, and the J commands should come from a script. So:
<session>+= bash-2.02$ cat rev-input.ijs |. (1!:1) 3 bash-2.02$ head -3 /etc/passwd | j rev-input.ijs J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. d70=: 0: = [ | [: i. [: # ] NB.Loc every xth item of y -- the J Phrases collection bash-2.02$
It appears as if nothing really happened. But the truth is that, as so long as the scripts are executed silently, neither (script) input nor evaluation results will be echoed.
The
-v
option will be more telling:
<session>+= bash-2.02$ head -3 /etc/passwd | j -v rev-input.ijs m56=: [: *./\ ' '"_ = ] NB.Locate leading blanks -- the J Phrases collection J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. NB. standard J profile NB. NB. [many things deleted here] NB. boot up J: 0!:0 <(1!:42''),'system/extras/util/boot.ijs' NB. ========================================================= NB. add your own commands here 0!:0 <'/home/pat/neitzel/lib/j/profile.ijs' |.(1!:1) 3 nigolon/nibs/:/:flesmih lived ehT:13:1:*:nomead hs/nib/:toor/:resurepuS niaga-enruoB:0:0:*:root hsc/nib/:toor/:tooR eilrahC:0:0:*:toor bash-2.02$
There are actually more noteworthy details hidden in this example:
- The
-v
option refers both to the explicitly mentioned rev-input.ijs script and the implicitly loaded profile script. It does not make the subscripts visibly, though, which are all called silently (using0!:0
).- The profile extension uses
2!:1 'fortune jphrases'
. 2!:1 is "asynchronous execution", and while its output goes to the stdout of the entire J execution, it will be merged with J's output at random points, and not necessarily even in one single chunk. As this session trace shows, it can even lead the output when it is flushed before the first buffered chunk of J script output. (During an interactive session, both programs respective stdout will be the terminal, and since that mostly implies only line-buffered output, it appears to be synchronized. Once you redirect the J output to files or other programs, the buffering into larger chunks becomes noticeable.The proper way to create output, however, is to use
1!:1
with the argument4
(for stdout) or5
(for diagnostics):
<session>+= bash-2.02$ cat rev-input-2.ijs data=.(1!:1) 3 (|. data) (1!:2) 4 bash-2.02$ head -3 /etc/passwd | j rev-input.ijs -- m147=: ~. NB.Delete repeated items -- the J Phrases collection nigolon/nibs/:/:flesmih lived ehT:13:1:*:nomead hs/nib/:toor/:resurepuS niaga-enruoB:0:0:*:root hsc/nib/:toor/:tooR eilrahC:0:0:*:toorbash-2.02$
If there is an error during script execution, something not so nice happens:
<session>+= bash-2.02$ cat rev-input-error.ijs uh_oh =. 'foo' , 44 data=.(1!:1) 3 (|. data) (1!:2) 4 bash-2.02$ head -3 /etc/passwd | j rev-input-error.ijs -- m5=: ur=: 2 _3&{. NB.Function to select upper right corner -- the J Phrases collection |domain error | uh_oh=.'foo' ,44 |[-0] root:*:0:0:Charlie Root:/root:/bin/csh |spelling error | root:*:0:0:Charlie Root:/root:/bin/csh | ^ toor:*:0:0:Bourne-again Superuser:/root:/bin/sh |spelling error | toor:*:0:0:Bourne-again Superuser:/root:/bin/sh | ^ daemon:*:1:31:The devil himself:/:/sbin/nologin |spelling error | daemon:*:1:31:The devil himself:/:/sbin/nologin | ^
The script execution aborts, and then J goes "interactive". Further J commands are expected from stdin, but that's the data now. The interpretation attempt just results into another error -- the sooner the better. If you are unlucky, it might even be parsed and executed "successfully":
<session>+= bash-2.02$ echo 'one two three' | j rev-input-error.ijs -- m6=: rfd=: 1r180p1&* NB.Radians from degrees -- the J Phrases collection |domain error | uh_oh=.'foo' ,44 |[-0] one two three one two three bash-2.02$
one two three
happens to be a perfect fork under J's implicit assumption that undefined names refer to yet-to-be-defined verbs.The really big problem here is how an error could be reliably detected. Consider the following pipeline, consisting of a simple data generator (
echo
), J as a central data processor, andtr a-z A-Z
as a simple lower to uppercase post-processor:
<session>+= bash-2.02$ echo 'one two three' | j rev-input-error.ijs -- | tr a-z A-Z M4=: +/ &. (*:"_) @ +. NB. MAGNITUDE | E.G. M4 3J4 -- THE J PHRASES COLLECTION |DOMAIN ERROR | UH_OH=.'FOO' ,44 |[-0] ONE TWO THREE ONE TWO THREE bash-2.02$
I would like to see at least some indication on
stderr
in a case like this. Compare:
<session>+= bash-2.02$ echo 'one two three' | tee /etc/passwd | tr a-z A-Z > xx tee: /etc/passwd: Permission denied bash-2.02$ cat xx ONE TWO THREE bash-2.02$
In this case you still notice that something went wrong. With J, you do not get any indication:
<session>+= bash-2.02$ echo 'one two three' | j rev-input-error.ijs -- | tr a-z A-Z > xx bash-2.02$
stdout
As of J Release 4.02, J's output is not what it appears. Here is our reversal script again. The profile is now sanitized again not to create any additional output.
<session>+= bash-2.02$ echo 'i. 2 3 4' | j J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. i. 2 3 4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 bash-2.02$
Now let's try to get rid of the copyright header. Deleting everything up to the first empty line should be all what is needed, right?
<session>+= bash-2.02$ echo 'i. 2 3 4' | j | sed '1,/^$/d' bash-2.02$Suprise. The empty lines aren't really empty. Here's the truth:
<session>+= bash-2.02$ echo 'i. 2 3 4' | j | vis J 4.02 Copyright (c) 1990-1998, Iverson Software Inc. All rights reserved. \^@ i. 2 3 4 0 1 2 3 4 5 6 7 8 9 10 11 \^@ 12 13 14 15 16 17 18 19 20 21 22 23 bash-2.02$
There are ASCII null characters interspersed at various places. This is a historical artifact and on the list of things to be phased out.
(1!:1) 1 vs (1!:1) 3 and (1!:2) 2 vs. (1!:2) 4
You might be wondering that the shell.ijs file on J4.02/Unix defines two output functions,
echo
andstdout
. What's the scoup behind having two functions here?
stdout =: 1!:2&4
(defined by the DoJ as "stdout output")
This was originally asked by me to be added to the language for J3.x/Unix along with (1!:2)&3 (stdin) and (1!:2)&5 (stderr).
I had to characteristics in my mind:
- I wanted to have defined ways to speak about redirectable IO as opposed to the session interaction ("programming").
- I was only thinking about character vector arguments, but I wanted to have these arguments taken faithfully.
In particular, I wanted to be able to dump
'abc'
to stdout without some extraLF
character. I am not really sure anymore, but I believe I can dimly remember a time when<session>+= 1!:2&4 ] i. 10would just yield a "domain error", but that was probably just some internal version prior to the first official release with it (j302/Unix).
As far as I was concerned, the stdin/stdout/stderr functions wouldn't necessarily have to run on numerical/boxed data, and they shouldn't have done any any formating on their own (in particular: linebreaks or empty lines indicating higher ranks; implied LF vs. LFCR vs. CR magic). I needed a way to output the literals, well, literally.
And J does so:
stdout 2 3 4 $ 'abc'
dumps exactly the 2*3*4 = 24 characters, without additional separators to stdout. Compare:
<session>+= $ 1!:2&4 ] 3 4 $ 'abc' abcabcabcabc3 4 $ 1!:2&2 ] 3 4 $ 'abc' abca bcab cabc 3 4The difference is even more striking with
1!:2&2
inserting hideous\0
characters between higher rank arrays in the output.I just learned today (five years later!) that
1!:2&4
in fact does not complain about non-character vector data. This is actually a confusing extra-feature for me. It is also not implemented correctly, as the following shows:
<session>+= m =. i. 3 4 stdout m 0 1 2 3 4 5 6 7 8 9 10 11 $ stdout m NB. some buffer is trashed... 3 4 stdout 'foo' NB. OK (side effect + return value) foofoo $ stdout 'foo' NB. OK foo3 toupper {. stdout 3 4 $ 'abc' NB. OK on rank-3, too abcabcabcabcABCA
echo =: 1!:2&2
(defined by the DoJ as "screen output")
This echoes any J object to the programming session (which happens to coincide with the stdout file descriptor with the jconsole. It could be "the current session window" in a hypothetical J GUI development environment. Or it could be an open channel to /dev/tty even when the stdout is redirected to some file.)
In order to display any J object, it must be formatted, and echo implies this formatting. Numerical data will be stringified according to Printing Precision settings proper column alignment, higher rank data will be output with appropriate linefeeds to indicate rows and matrixes, and a LOT of tunable formatting work goes into boxed data.
Strictly speaking, i.e. according to the DoJ,
1!:2&2
wouldn't have take care of non-literal data. But it does, and as it does helpful line-feed stuff for numerical matrices, it does so for character matrices, too, .... but unlike1!:2&4
, with the additonal formatting features adding the line breaks. I am deliberately saying "line breaks" here, not "linefeeds": "echo" is concerned about display, not about characters.The way I interpret the Dictionary, nothing therein requires that
1!:2&2
generates characters anywhere. As long as you can see something "on the screen", the job is done. As it happens, a lot of1!:2&2
output is based on":
which does generate character data (but no linefeeds yet, either!). It is "echo" which than adds line breaks using LF or CRLF or CR characters -- whatever is needed, as appropriate for the1!:2
destination. Things could be radically different, though. For example, a J implementation could display boxed data without any characters at all, plotting the boxing lines. It could display a four dimensional array in a fly-through OpenGL animation.
Suggested changes
- Do not create spurious NUL or other characters.
- Emit a final newline when isatty().
- Send errors to stderr.
- Set the exit status upon error.
- Have an option to suppress the profile (?).