Juggle Home - Bits'n'Pieces - Feature Hitlist - Problem Reports - Mailing lists - The J Repository - References +-------------------+ | 9!:12'' | |5 | +-------------------+

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 to stderr:

<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 that stdin 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 and cron 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" code 1, 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 proper way to create output, however, is to use 1!:1 with the argument 4 (for stdout) or 5 (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, and tr 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 and stdout. 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:

  1. I wanted to have defined ways to speak about redirectable IO as opposed to the session interaction ("programming").

  2. 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 extra LF character. I am not really sure anymore, but I believe I can dimly remember a time when

<session>+=
        1!:2&4 ] i. 10

would 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 4

The 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 unlike 1!: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 of 1!: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 the 1!: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

+-------------------+ | 9!:12'' | |5 | +-------------------+ Juggle Home - Bits'n'Pieces - Feature Hitlist - Problem Reports - Mailing lists - The J Repository - References