User:Ric Sherlock/Temp/InteractivePrompt

From J Wiki
Jump to navigation Jump to search

Interactive User Session

This is my attempt to summarize an April 2007 forum thread on how to ask for, and process input from, the user. Please feel free to improve it. Maybe it could end up as a FAQ?

Character-based user prompts

In simple terms such a session might look like this:

Please enter some numbers: 1 56 41 23
The sum of the numbers is 121

The general feeling of the thread was that this sort of usage (read/evaluate/print/loop or REPL) is a relic from the past, and is both clumsy and inefficient. The following is worth quoting!! "This prompt+manual input notion is a lousy paradigm that is encumbered by widely-accepted, unnecessary limitations of the past 50 years. Today, we have scalpels and laser beams but most people still insist on using sticks and rocks."

In addition there are good reasons that J is not an ideal language for creating such a program. Nevertheless it is certainly possible.

A script to generate the session above might look like this (only in J version 6):

load 'misc'
main=: 3 : 0
  tst=. prompt 'Please enter some numbers: '
  tst=. 0 ". tst                                 NB. convert the string to numbers
  'The sum of the numbers is ',": +/ tst         NB. calculate & return sum

To load and run this code, copy it to a new *.ijs file. In the J session (or at the J console) type

load 'filepath/filename.ijs'
main ''  NB. that is two single quotes, not one double quote!

For version 7 J, the following definition works but does not the allow input on the same line as the prompt:

prompt=: 3 : '1!:1]1 [ ((2) 1!:2~ ])y'

Example usage:

   nm=. prompt 'Enter name: '
Enter name:

For a session that returns multiple lines, and has multiple prompts, a slightly more complex version might be:

load 'misc'

main=: 3 : 0
  tst=. 0 ". prompt 'Enter a list of space-separated numbers: '
  smoutput 'numbers: ',": tst          NB. "smoutput" writes the result to the screen
  smoutput '    max: ',": >./tst
  smoutput '    min: ',": <./tst
  smoutput '   mean: ',": (+/ % #) tst
  tst2=. 0 ". prompt 'Give me more numbers! '
  smoutput 'numbers: ',": tst2
  smoutput '    sum: ',": +/tst,tst2
  'the end'

Here is an alternative, more free-form, method of interacting with the user:

load 'primitives'

guidance =: 0 : 0
Enter a sequence of number pairs
Conclude with a left parenthesis
in the first position in a row

main =: 3 : 0
guidance  1!:2 (2)
mydata =. 0 : 0
NB.  convert mydata to a matrix
moddata =. do open  box  cut _2  mydata
NB.  moddata is now a numeric array with two columns
NB.  and you can do what you want with it
NB.  If you write the commands correctly
NB.  they will produce an array of the output wanted
NB.  with a row for each column
statfns rank 1 transpose moddata
maxval =: max insert
minval =: min insert
range  =: maxval - minval
statfns =: maxval,minval,range

And more complex again. The user is asked to provide the result to a simple arithmetical problem. The user response is evaluated and the session repeats until the user response is to only press the Enter key. At which point the session is terminated.

require 'misc'  NB. prompt

main=: 3 : 0
  while.1 do.
    'x y'=. ":&.>3+2?@$10
    'op'=. ({~ ?@#)'+-*'
    if. 0=#z=. 0".prompt (exp=. x,op,y),' = '
    smoutput (10#' '),(z=".exp)>@{'Wrong';'Correct!'

Also check out Dan Bron's implementation of REPL.

GUI-based user prompts

You may prefer to use GUI prompts to interact with the user. Here is one example of how to do this:

require 'jinput jselect'

main=: 3 : 0
  title=. 'Enter numbers separated by spaces.'
  empty 'jinput' conew~ title;'3 4 56 2';'input_result'

input_result=: 3 : 0
  res=. y;(+/ % #)0 ". y  NB. mean
  smoutput >":each res

  genders=. ;:'Ewes Rams Wethers'
  title=. 'What gender animals are the data from?'
  empty 'jselect' conew~ genders;title;0;'writeGender'

writeGender=: 3 : 0
  if. 0=0{::y do. return. end.
  smoutput 2{::y

Also check out this example of opening a window to query for a source and destination directory and wait for the user to finish inputting them.


The major problem with REPLing in J is that you can't do it from within a script. Notice that your instructions force the user to type main '' in the session. This has important implications.

Primarily, it means you can't automate the REPL. You couldn't, for example, create a REPL program in J such that the user only need double-click an icon on his desktop to be greeted with your friendly prompt. You'd have to instruct him to type main '' every time he started your program. Worse, this would be required of automated tools as well as human users.

The second implication is that by requiring the user to type J, you trust him with complete access to your J system. That first line he types in in the J session needn't be main''. Since he has access to the J session, he can type anything at all, and you won't have the ability to validate his input.

This is merely a usability problem when your program is local to the user's machine (e.g. if he mistakes '' for " and is confused by the lack of response). But, if you're running a "J service", then the J session is running on your server -- and has access to your file system. There's nothing stopping a malicious user from typing , e.g., 2!:0 'rm /'.

My REPL script mitigates this problem a bit, because it means the user needn't (can't) type anything in to the J session. If you load a REPL script, and the call foo repl '' is the last line executed in the script, then your program will run automatically and will immediately prompt the user for input.

But there are still problems outstanding:

A. You still cannot prompt the user for input in any context except the top-level. That is, it is impossible to prompt for input while you're in a callstack. A verb cannot call another verb that prompts for input, process that input, and moves on. A. All prompts in J are interruptible, and those interrupts cannot be intercepted (well, I haven't attempted try. yet). So, if you have a command-line REPL, and the user presses CTRL+C, he gets access to the J session, with all the security implications outlined above. And you can't stop him. Similarly for jbreak which interrupts buth jconsole and the J session manager (though you could solve that by setting the break file to an empty (or secret?) value). A. J was not designed for this. J is block oriented, not stream oriented.

So, I think Devon was right when he said that if you're already in the J Session Manager, then you're in a GUI context, you have GUI resources, and would be best off creating a wd GUI to interact with your user (rather than trying to press the session manager into service, essentially by turning its GUI into a console app; I really think people only try this scheme because they believe it will/ought to be trivial).

If you're creating a J console app, then your purpose is probably to automate a task, and so you probably don't need a REPL; rarely do automated tasks need prompts or to read input line-by-line.

Another line of attack would be to have J be a socket server (which both the console and the session manager support well), and to create a seperate utility which would send its stdin to J's socket, and and J's socket output to its own stdout (like an inverse to inetd, let's call it tenid).

-- Dan Bron <<DateTime(2007-04-17T16:11:18Z)>>