Default kulla messages

Paul Sandoz paul.sandoz at oracle.com
Fri Jun 26 17:51:26 UTC 2015


I think this sets the right precedence, aim towards what we think the steady state should be, with the options for more verbosity if need be for new users.

The degree of output hits a good balance.

I am tempted to suggest an even more terse approach, such as:

-> int x = 0
| int x = 0

-> x    // <- this is not the current behaviour in the current REPL
| int $1 = 0

-> x + 1
| int $2 = 1

-> int y = x + 1
| int y = 1

-> int m() { return 1; }
| int m()

-> m()
| int $3 = 1

-> class A { void m() {} }
| class A

-> new A()
| A $4 = A at 6842775d

-> enum E { ALPHA, BETA; }
| enum E

-> java.util.stream.IntStream.range(0, 100).mapToObj(Integer::toString).collect(java.util.stream.Collectors.toList())
| List<String> $5 = [0, 1, 2, 3, 4, 5, 6, 7]

It might be a little too terse, but i like the way i can observe the variable type, name, and value on one line. I can easily infer that the REPL created a new variable for me.

Either way we might need smarter toString output if the resulting string length is too large .

Paul.

On Jun 26, 2015, at 6:37 PM, Brian Goetz <brian.goetz at Oracle.COM> wrote:

> I've gotten a few private responses on this so let me summarize them.
> 
> There's a range of opinions on whether the default behavior should lean towards "onboarding new users".  We've faced this decision before in other contexts, and our default orientation is that we should design for the longer-term steady-state, and avoid over-rotating towards highlighting what is new -- since new is a temporary state.  In other words, build the UX so that it serves the intended use cases, not necessarily the "teach people to use the REPL case."  (There are other means of educating new users.)
> 
> In surveying a number of REPLs, the common theme is that their output is terse.  In some cases, its because there's less information to display (some languages don't have types, so they don't display those), but even in the Scala case, where there's plenty of type information, the REPL UX is "just the facts."  I think we would need a good reason to buck this trend.
> 
> Some pointed out that the user can change the verbosity level.  Which is true -- and a great feature -- but let's admit the reality that 99% of users never will; they'll just type 'jshell'.  So the default UX needs to serve the broadest range of users.
> 
> There's several categories of output that the REPL might report.  Let's enumerate them.
> 
> 1.  Expression results.  (If the input was an expression, we probably always want to print this; if its not an expression, there's nothing to print.)
> 
> 2.  Text sent to System.{out,err}.
> 
> 3.  Diagnostics either from compiling the input or from executing a REPL command.
> 
> 4.  Runtime exceptions produced during execution.
> 
> 5.  Side-effects on the REPL state (vars/methods/classes declared/invalidated/dropped/changed state.)
> 
> Some of these are mutually exclusive; if there's a compilation error, there'll be no evaluation.
> 
> Here's what I am proposing for the default scheme:
> 
> (3) is printed first -- and we stop here (unless the diagnostic is merely a warning)
> (2) is printed as it is generated, with no decoration
> If the input was an expression, (1) is printed, prefixed by something like "=> "
> If an exception occurred, (4) is printed
> (5) is printed, parenthetically.
> 
> For (5), there are sub-categories:
> a) a new temporary variable was created
> b) a new variable was created (var declaration)
> c) a new method/class was created
> d) a variable/class/method was invalidated
> e) a variable changed its value (assignment to existing var)
> 
> By default, I'm suggesting we should print something for (a) and (c), and maybe (b) but not for the others.
> 
> Key to this is: other schemes can change the verbosity of individual elements ("created $1" vs "created temporary variable $1 of type int") or whether elements are printed at all, but should not change the overall structure.
> 
> Some examples:
> 
> repl> 1 + 1
> => 2
> (declared temporary variable $1 of type int)
> 
> repl> int x = 3
> (declared variable x of type int)  // maybe
> 
> repl> x + 1
> => 4
> (declared temporary variable $2 of type int)
> 
> repl> if (x == 3) x = 4;
> // statement -- no result, no structural side-effects
> 
> repl> int m() { return 43; }
> (declared method m)
> 
> repl> m()
> => 43
> (declared temporary variable $3 of type int)
> 
> repl> /drop m
> (dropped method m)
> 
> repl> /drop m
> error: cannot drop m, does not exist
> 
> repl> $1/0
> java.lang.ArithmeticException: divide by zero
> ... stack trace ...
> 
> repl> System.out.println("Hello World")
> Hello World
> 
> 
> Some felt that we should print type and value for every assignment. But, let's not forget that the user can always ask for these if they want, just by evaluating the variable:
> 
> repl> x = f(...)
> 
> repl> x
> => Foo[blah, blah ]
> 
> So I think we should err on the side of *not* printing values that are not the result of evaluating a top-level expression.
> 
> 
> 
> 
> On 6/22/2015 5:40 PM, Brian Goetz wrote:
>> The messages produced by JShell during the course of
>> evaluation/declaration have been in a placeholder state for a while, and
>> its probably time to paint the bikeshed of what the default messages
>> should look like (there are multiple message schemes, which can crank up
>> and down the verbosity, but the default set is the most important.)
>> 
>> Here's what JShell prints now for an expression and a method declaration:
>> 
>> -> 1 + 1
>> |  Expression value is: 2
>> |    assigned to temporary variable $1 of type int
>> 
>> -> void foo() { }
>> |  Added method foo()
>> 
>> These are all useful bits of information:
>>  - the result of the expression, if any
>>  - side-effects on the REPL state
>>    - temporary name and type, for expressions
>>    - var/method/class name and type, for declarations
>> 
>> However, plenty of people have commented that these messages are "kind
>> of verbose".
>> 
>> The Scala REPL output contains all this information too, but more
>> compactly:
>> 
>> scala> 1 + 1
>> res1: Int = 2
>> 
>> scala> def m() { }
>> m: ()Unit
>> 
>> The Ruby IRB output is more taciturn:
>> 
>> irb(main):001:0> 1 + 1
>> => 2
>> irb(main):002:0> def m()
>> irb(main):003:1>     puts "Hello"
>> irb(main):004:1> end
>> => nil
>> 
>> The Perl re.pl even more so:
>> 
>> $ 1 + 1
>> 2
>> $ sub m {
>> >     return 1;
>> > }
>> (empty line)
>> >
>> 
>> 
>> While we could gather more examples, I think there's a pattern emerging:
>> expression evaluation should put the result front-and-center.
>> 
>> Here are some possible options for the evaluation of 1+1 in jshell:
>> 
>> --
>> 2 (assigned to temporary $1)
>> --
>> 2 (assigned to temporary $1 : int)
>> --
>> $1 (int): 2
>> --
>> => 2
>> (assigned to temporary $1 : int)
>> --
>> int: 2
>> (assigned to temporary $1 of type int)
>> 
>> There are obviously many more variations.
>> 
>> I propose using, as the default level:
>> 
>> => expression value
>> 
>> to indicate the result of an expression, with statements producing no
>> output, coupled with a convention to put all side-effect-on-REPL-state
>> information in parentheses on its own line:
>> 
>> repl> 1 + 1
>> => 2
>> (assigned to temporary $1 of type int)
>> 
>> repl> void m() { }
>> (defined method m())
>> 
>> repl> /drop m
>> (dropped method m())
>> 
>> repl> int x = 3
>> (declared variable x of type int)
>> 
>> repl> x = 4
>> (no output)
>> 
>> 
>> I think this scheme captures the essential information without too much
>> noise (users can select a higher verbosity level if they want.)
>> 



More information about the kulla-dev mailing list