Friday, April 30, 2010

Running Mono C# REPL on Windows

Being a big fan of programming language REPLs and command-line consoles, I have awaited one for C# that I can easily install and comfortably use for lightweight validation of C# syntax and for exploring its language features. Originally, Snippet Compiler had filled that role and then replaced with LINQPad but neither one is a REPL.

Fortunately, Mono, the open source project for .NET and C# that offers cross OS platform support especially for Linux and Mac, does have an interactive shell (CsharpRepl) for evaluating C# statements and expressions. Since Mono also runs on Windows, I downloaded and installed the latest Mono release 2.6 on my Windows development machine.

Once installed, to run the C# REPL simply:

  1. Select Start ->; All Programs -> Mono 2.6.1 for Windows -> Mono-2.6.1 Command Prompt
  2. At the command prompt, type "csharp"

The first step just adds Mono's bin directory to your environment PATH for the current shell session. (Alternatively, you can navigate via the command line to the Mono bin directory and directly run the file, csharp.bat.)

The C# REPL works if used within the Windows shell, cmd.exe (a.k.a. "command prompt") but with some caveats. The first immediate one is the command prompt text "csharp >" is not displayed making it a bit disorienting to use. It's difficult to distinguish between the input and the output of your expressions. [Update: This bug was subsequently fixed and it is now available with the version 2.6.7 release.]

Another is no autocomplete functionality but this feature is only available in Mono's GUI command console, 'gsharp'. GSharp requires an additional install of the mono-tools package available on the Windows platform download page under the link named "Gtk# for .NET". Once installed, to launch gsharp:

  1. Start -> All Programs -> Mono 2.6.1 for Windows -> Mono-2.6.1 Command Prompt
  2. c:\> gsharp

To activate autocomplete, type in part of a word and then hit <TAB>. Sometimes auto-completing words is slow in gsharp particularly if it has to search a large set of libraries. For example, typing the text "using Sys" and then <TAB> causes hanging since 'System' is the most top level .NET lib.

I decided to stick with using gsharp (a.k.a. the "C# InteractiveBase Shell"?) over csharp plus cmd.exe combo not only because of the autocomplete feature but because it also does display a "csharp >" prompt.

With those two issues resolved in gsharp, I continued to explore how the csharp REPL performed and behaved. The most striking deficiency I then encountered was when typing a statement containing invalid syntax, it did not show any output results. This was surprising since it goes against what I consider to be one of the hallmarks of a good REPL: immediate feedback not just on command/code that evaluated successfully but on things that failed to evaluate properly. Strange the lack of output...almost as if it was missing the 'Print' in REPL.

With continued use, I noticed in the examples found in the Mono REPL documentation that each expression or statement requires the ";" character at the end of it to produce any visible output. Otherwise, it gets ignored. I was expecting the same behavior found in the Visual Studio's Immediate Window where ";" is not always required. Not sure what the advantage of having to always type ";" (other than as a constant reminder that you are using a C based language in a REPL). Just seems like an extra unneeded keystroke.

However, reading more of the REPL docs, it implies multiple declarations can be made on a single line using ";" as a delimiter:

csharp> var a  = "why so many semi-colons?"; 5; "more stuff!";
"more stuff!"
csharp> a;
"why so many semi-colons?"

All three statements get evaluated but only the last one ("more stuff!") is printed to the screen.

The docs also explicitly state the inverse: one declaration ending with ";" can be spread across multiple lines:
"...Statements and expression can take multiple lines, for example, consider this LINQ query that displays all the files modified in the /etc directory in the last week. The prompt changes from "csharp" to " >" to indicate that new input is expected..."
"...Multi-line input...If your code does not fit in a single line, you can enter expressions in multiple lines. The shell will not execute the code until a valid expression has been entered or a syntax error is flagged. A special prompt is shown to indicate that ics is waiting for input..."

Although the docs states that multi-line input is supported, it does not seem to be true of my current install on Windows. On Linux, I can do this:

csharp> var list = new int [] {1,2,3};
csharp> var b = from x in list
   >    where x > 1
   >    select x;
csharp> b;

On Windows, however, the special indented continuation prompt "  >" never appears when a new line is returned if the ";" character is not included. This is unfortunately a notable flaw in the REPL tool on the Windows platform.

In addition to the online documentation, another source describing the more common commands is available in the REPL itself. Just type "help;":
"Static methods:
Describe(obj)      - Describes the object's type
LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)
LoadAssembly (ass) - Loads the given assembly (like -r:ASS)
ShowVars ();       - Shows defined local variables.
ShowUsing ();      - Show active using decltions.
Prompt             - The prompt used by the C# shell
ContinuationPrompt - The prompt for partial input
Time(() -> { })    - Times the specified code
TabAtStartCompletes - Whether tab will complete even on emtpy lines
Of course, a couple of these commands exhibit some quirks. If type 'ShowUsing();' it does not display anything in the console although it is expected to do so (it behaves like this on Linux). After trying the command several times, I looked at the original command prompt window from which gsharp launched and saw the results of the command showing in there. The same was true with 'ShowVars()'. Recommend keeping the command prompt console in view while using gsharp to see any output piped outside of it. (The issue appears to been logged at the mono project as a bug.)

Perhaps to truly escape all of these OS specific limitations, I might be better off running Mono's REPL within a Linux VM on Windows. However, this approach negates the benefits of a cross-platform framework that Mono aspires to be.

Despite these minor difficulties, Mono's C# REPL has been a nice addition to my .NET development toolbox. It has proved useful when I needed a deeper understanding of how delegates and lexical closures behave in C# and when figuring out how to do list comprehensions in C# using List<T>.ConvertAll instead of LINQ. It provides a quick, frictionless way of observing and interacting with the functionalities of C# and the .NET libraries.