UI review: Key.id() was: jshell commands

Brian Goetz brian.goetz at oracle.com
Thu Jul 2 14:56:05 UTC 2015


Stepping back...

What this discussion underscores is that there are several "keyspaces" 
being conflated.

1.  The REPL needs a unique key for each snippet, so that the client 
(command line shell, IDE) can retrieve information about entities 
persisted in the REPL state.  Internally, a numeric ID is fine for this; 
the API wraps this with a Key abstraction.  Good so far.

2.  The command-line shell user has a notion of "what I typed".  She may 
wish to see a history listing, filtered in various ways (successful 
entries, live entries, etc.)  Further, she may wish to name a specific 
entry, say to edit/drop it, or to re-execute it.  So there needs to be a 
simple token that the user can type to name each entry.

It seems reasonable to believe that user expectations will largely be 
that these are simple, "sequential" and "start from 1".  (These terms 
are squishy and subject to many asterisks.)  Systematic departures from 
these expectations (e.g., random keys, untypable keys, keys starting 
from 349864562) will cause user anguish.

This is related to, but not identical to, the command line recall 
history; hitting up-arrow in the command-line shell cycles through 
things that have been typed.  (This is clearly in the domain of the 
command-line shell.)

3.  There's also a keyspace of temporary evaluation results.  There are 
a few ways to go with this; we could have this be independently 
numbered, starting from 1 (current implementation), which is pretty 
sensible.  It's also been suggested that we could try and number this in 
parallel with (2); this is also sensible, and has different 
pluses/minuses.

I think the missing bit of design here is that we never thought too 
deeply about whether the "numbers" in (2) / (3) should be under the 
control of the API or the client (command-line shell, IDE, etc).  My 
first thought is that, since these are things we expect the command-line 
shell user to *type*, then they should be generated by the shell. 
(Different UI metaphors might want to call them different things.)  And 
this is not the path the current implementation takes, which I think is 
where this pain comes from.

> But now let’s apply your suggestion to the example/challenge I proposed --
>
> Start-up:
>
> 	int x = 5;
>
> Interactive:
>
> 	snurble snurble;
> 	int y = 3;
> 	int x = y * y;
> 	/list
>
> With a straight key mapping and two tool-id namespaces, /list would show:
>
>   2:  int y = 3;

I think users will be surprised to not see an entry for x here.  (Though 
I'm not nearly as bothered by the missing "1"; they did type something 
bogus, and if they didn't type something bogus (the common case), things 
would start cleanly from 1.)

> If we create a new tool-id for each snippet (thus having many tool-ids to one Key/id()) we could get:
>
>   2: int y = 3;
>   3: int x = y * y;

I think this is more likely to make sense to the user.

> but we still aren’t starting at 1 because of the bogus input. If bogus input also has its own namespace it would be what we “want”, where /list all would be:
>
> bogus.1:snurble snurble
> 1: int y = 3;
> 2: int x = y * y;

The "bogus stuff is a separate scope, like startup" is a reasonable 
approach, but either way I'm not really bothered by this anomaly.  I 
think I prefer giving "snurble" its own normal id (even if its filtered 
in the standard list view) because then the user can /edit 1 and correct it.

> Since we can’t reasonably hide/transform the generated name, the
> only
approach I could see for aligning temp var names with tool-ids is to add
a callback to the API to generate the temp var name. Maybe:
>
>    void setTempVariableNameGenerator(BiFunction<Integer, Integer, String> generator);
>
> Kinda ugly, but it should do the job.

Given the argument above that the tool should be in charge of generating 
the user-visible IDs, this seems a reasonable choice.  We can explore 
how to make the API a little less warty -- there's options here.



On 7/2/2015 2:47 AM, Robert Field wrote:
>
>> On Jul 1, 2015, at 3:31 PM, Brian Goetz <brian.goetz at oracle.com> wrote:
>>
>>> All the above are API level.  The API doesn’t know anything about start-up files or list filtering. Or commands for that matter.
>>
>> And, the mapping from "id" as seen by the command-line shell in commands like /drop need not match the IDs in the API.  This discussion is about "what should the user see.”
>
> Absolutely, we can map Key or id to anything we want, but we would need to do so keeping the semantics I outlined in mind.
>
>>
>> The user sees that every command entered has a (currently numeric) "handle", and some commands will take such handles as input.  /list and /list all shows known snippets along with their handles; /list all shows more snippets than /list.
>>
>> Still, from a user-model perspective, I think it's a bug that
>>
>> repl> 1 + 1
>> repl> 2 + 2
>> repl> /list
>>
>> yields
>>
>>   9: 1+1
>>   10: 2+2
>>
>> It's a total WTF for the user.
>
> Yes it is.  And there are two ways about that.  Best, of course, would be to eliminate the problem.  Next best is to explain it by some means (like your elided note)
>
>>
>> There are a few reasons why something doesn't show up in /list:
>> - It was part of the startup
>> - It was a bogus snippet
>> - It replaces an existing key
>>
>> Is that complete?
>
> Or it was dropped.
>
>>
>>> (5) /list, by default, shows interactively entered active (not failed, dropped, or withdrawn) snippets in id() order.
>>> (6) That means, even if there are no start-up entries (-nostartup), /list won’t necessarily start with “1” since your first entries may have failed.
>>> (7) The last entered snippet might not be the last listed in /list if it has the same Key of a previously entered snippet.
>>> (8) /list all — shows snippets from the start-up, from /open, it shows failed, dropped, and withdrawn snippets.
>>
>> None of these seem like good arguments against the proposed approach. If the user enters well-behaved snippets, snippets will start at (1), and temporaries will line up with "line numbers".  The /list header text can be amended to:
>>
>> repl> /list
>>
>>     [ some entries suppressed, enter /list all to see ]
>>     1: 1+1
>>     3: $1 + 1
>>
>> repl> /list all
>>
>>     startup.1: import java.util.*
>>     1: 1+1
>>     2: blargle(*$#$^N%#$)(
>>     3: $1 + 1
>>
>> possibly adorning "2" with something indicating its status.
>
> But now let’s apply your suggestion to the example/challenge I proposed --
>
> Start-up:
>
> 	int x = 5;
>
> Interactive:
>
> 	snurble snurble;
> 	int y = 3;
> 	int x = y * y;
> 	/list
>
> With a straight key mapping and two tool-id namespaces, /list would show:
>
>    2:  int y = 3;
>
> Because /list all would be
>
>    startup.1: int x = y * y;
>    1: snurble snurble;
>    2:  int y = 3;
>
> If we create a new tool-id for each snippet (thus having many tool-ids to one Key/id()) we could get:
>
>    2: int y = 3;
>    3: int x = y * y;
>
> but we still aren’t starting at 1 because of the bogus input.  If bogus input also has its own namespace it would be what we “want”, where /list all would be:
>
>    bogus.1:snurble snurble
>    1: int y = 3;
>    2: int x = y * y;
>
> (since the user should be able to use the start-up or bogus tool-ids, and since they should be differentiated from program identifiers, these should probably be suffixes and shorter)
>
> But now we have a new problem, if the API uses id() to generate temp variables they won’t line up at all.
>
> Since we can’t reasonably hide/transform the generated name, the only approach I could see for aligning temp var names with tool-ids is to add a callback to the API to generate the temp var name.  Maybe:
>
>     void setTempVariableNameGenerator(BiFunction<Integer, Integer, String> generator);
>
> Kinda ugly, but it should do the job.
>
> -Robert
>
>


More information about the kulla-dev mailing list