A major complaint about Java in the context of today's programming languages is that you need to go through the full process of writing a class with a main method, compiling it, and then executing it just to run a single line of code. As such, one of the most useful features of Scala is the REPL (read-eval-print loop) because it gives you that scripting language feel and allows for very quick experimentation. I decided to play around a bit with the innards of the Scala REPL, whose implementation lives in the scala-compiler jar. Because the classes used to build the REPL are exposed, there are some interesting possibilities for programmatically examining and altering the environment.
The main class that the REPL uses is ILoop, which I imagine stands for "interpreter loop" or something to that effect. It is quite simple to embed the normal REPL into our own program, like this:
If you run this code, you'll be presented with the standard prompt that you see when you launch the REPL (note that without the jline library you won't get command history, tab completion, etc, but the compile-and-run functionality is there). It is running with the same classpath as your program, so any classes you have defined are available in the interpreter. Let's do something a bit more complex; what if we want to programmtically initialize the interpreter environment, e.g. to have certain variables pre-defined or classes pre-imported? We can extend the ILoop class and override the postInitialization() method where we can handle the newly created IMain object, which is the actual interpreter:
Here, we bind the variable SomeConstant to have value 42, pre-import the Foo class, and print out the list of symbols defined by the interpreter. Because we have access to the interpreter object, it is easy to see how you could set up the proper environment with the interpreter and make Domain-Specific Languages (DSLs) even more natural. When you start the custom ILoop from above, we see the following output:
So our constant is defined, the Foo class is imported, and we see that the two defined symbols in the interpreter environment are SomeConstant and this special $intp variable. It turns out that this is a reference to the actual interpreter object that we had earlier, which lets us do crazy meta things like interpret a line of code which interprets a line of code. Here's a transcript from the interpreter environment:
So we see that the implementation of the Scala REPL is quite extensible in terms of altering the environment from code. I think there are a lot of applications of this, especially when using Scala outside of the traditional software engineering environment.
No comments:
Post a Comment