CSharpRepl
CSharpRepl copied to clipboard
Add automatic variables that represent previously evaluated values
Feature Description
It'd be nice to have automatic variables that represent previous prompt results.
There are two possible approaches (they aren't mutually exclusive):
Counting Forwards
Count forwards from the start, e.g. line0
, line1
, line2
, etc. We'd probably want to change the prompt from >
to lineN >
so it's easy to see what a given line would reference. A downside of this is that it's a more complex UI:
line0> 5 + 3
8
line1> 17 * 2
34
line2> line0 + line1
42
Probably want a command line switch to disable this feature, in case people don't want the extra noise.
Counting backwards
Have automatic variables that always represent the previous line, and the line before that (e.g. last
, last2
, etc).
- A benefit of this is that it wouldn't require a change to the prompt
- A downside is that it can get confusing to figure out what you've historically run, as the variables will have a different value on each line. It also somewhat breaks history, because if you run a line from history that references these
last
variables, the result will be different.
I'm currently planning to implement only Counting Forwards
To my knowledge, roslyn scripting does not support dynamically adding to the ScriptGlobals type, so it might be a bit tricky to implement. We'd also want to ensure that these lines show up in intellisense.
I quite like the way IPython does this: https://jakevdp.github.io/PythonDataScienceHandbook/01.04-input-output-history.html
tl;dr: _
references the last result (same as in the normal Python REPL and Ruby's irb), __
the second to last, and ___
the third to last. Additionally, all outputs can be referenced via Out[n]
.
Underscore syntax would interfere with C# discards (https://docs.microsoft.com/en-US/dotnet/csharp/fundamentals/functional/discards). That cannot be used.
But I probably like the Out[n]
syntax.
Both Ruby and Python use _
for discards in normal code but also for the last result in the REPL, hence the suggestion. In both languages _
is just a normal identifier though. Ruby for example reassigns _
after each expression was evaluated:
irb(main):001:0> a, _, c = [1,2,3] # assignments return their RHS
=> [1, 2, 3]
irb(main):002:0> _
=> [1, 2, 3]
irb(main):003:0> a
=> 1
irb(main):004:0> c
=> 3
irb(main):005:0> _
=> 3
It's very possible that the same wouldn't work in C# though, I didn't check that. Out[n]
is probably the better choice anyway.
E.g. this code won't compile when _
is redefined because of types mismatch:
public void M(int _)
{
_ = DateTime.Now.ToString();
}
While this compiles successfully:
public void M()
{
_ = DateTime.Now.ToString();
}