What REPL means for Java

Coming in Java 9, JShell will change how developers learn and write Java code

Maybe you're a closet Clojure or Scala developer, or perhaps you've worked with LISP in the past. If so, there's a good chance you've used a REPL as a part of your daily routine. REPL, or read-eval-print-loop, is a shell interface that reads each line of input, evaluates that line, and then prints the result. Instant feedback, nice!

When you use a REPL, you are writing code interactively and executing it without delay. The release of Java 9 in 2016 will deliver a fully functional REPL environment named JShell (code-named Kulla). This article provides an overview of the Java REPL and discusses some possibilities for how you might use it in your Java programming -- yeah, you!

Wait, Java doesn't have a REPL already?

Surely an established language like Java must have a REPL! Well, actually, not all languages have them and Java is one of those that has been missing it. Arguably, with more ceremony and boilerplate than most languages, Java is one of the languages whose developers have deserved it most. Java has had something a bit REPL-like for a while in the form of Java BeanShell, but this project was never a fully featured REPL on par with those of other languages. It was just a subset of the full Java language syntax.

REPL reduces turnaround

Shortening turnaround time and feedback loops as much as possible is incredibly important to a developer's sanity. A REPL is a great tool for developers wanting to achieve just that. Developers are most productive when they can see the results of their work immediately. With a Java REPL, developers will be able to write code, execute that code, and then continue to evolve their code on-the-fly without having to exit in order to run a build and so forth. While many of the systems using Java are beyond the complexity of what could be handled by an interactive REPL, the mere presence of a REPL in the JDK means that someone, somewhere will find an amazing use case for it in no time. The fact that JShell exposes an API basically ensures that IDE developers are going to integrate this REPL into the tools we use to write code. Just wait till the Java REPL is part of every IDE!

Get started with JShell

It's important to realize that using the in-development REPL, Project Kulla, is not for the faint of heart. Kulla, aka JShell, isn't part of the JDK 9 preview bundle at the time of writing, so you'll have to clone a Mercurial project, compile the JDK, and compile JShell yourself. Set aside an hour for this process, especially if you aren't normally throwing around JDK source code. You'll have to disable warnings as errors, and if you are building on OSX make sure that you've installed XCode along with XQuartz for the freetype library. Follow the instructions below to install and run Project Kulla in your Java development environment.

1. Install Java 9

To run JShell you'll need to download and install the latest early access preview build for Java 9. Once you've downloaded Java 9 set your JAVA_HOME environment variable and run java –version to verify your installation. This can be a pain, particularly on OSX, so it's worth double checking!

2. Install Mercurial and Project Kulla

Project Kulla is an OpenJDK project so you'll have to clone the Mercurial repository to compile it.

Next you'll clone the Kulla repository:


hg clone http://hg.openjdk.java.net/kulla/dev kulla

Then you'll configure the build:


cd kulla
bash ./configure --disable-warnings-as-errors
make images

3. Compile and run REPL

Here's the code to compile REPL:


cd langtools/repl;
bash ./scripts/compile.sh

And here's the code to run it:


bash ./scripts/run.sh 

As I noted, Java's REPL feature isn't ready for general consumption yet, but we can still take it for an early test drive!

You do the math

For an initial example of what JShell can do, let's evaluate some simple expressions using java.lang.Math:

Listing 1. Evaluating mathematical expressions with REPL


$ bash ./scripts/run.sh 
|  Welcome to JShell -- Version 0.710
|  Type /help for help

-> Math.sqrt( 144.0f );
|  Expression value is: 12.0
|    assigned to temporary variable $1 of type double

-> $1 + 100;
|  Expression value is: 112.0
|    assigned to temporary variable $2 of type double

-> /vars
|    double $1 = 12.0
|    double $2 = 112.0

-> double val = Math.sqrt( 9000 );
|  Added variable val of type double with initial value 94.86832980505137

Here we're evaluating expressions, finding the square root of a number, and then adding two numbers together. This isn't the most complex code, but you should note that the /vars command gives us the ability to list the variables created in the JShell session. We can refer to values of unassigned expressions using a dollar sign ($) notation. Lastly we can create a new variable and assign it a value.

Define a method

Now it gets more interesting. In this example we define a method to calculate the Fibonacci sequence. After the method is defined, we check to see which methods are defined with the /methods command. Finally, we execute a snippet of code to loop through an array and print out the first few numbers in the sequence.

Listing 2. Calculate the Fibonacci sequence


$ bash ./scripts/run.sh 
|  Welcome to JShell -- Version 0.710
|  Type /help for help

-> long fibonacci(long number) {
>>       if ((number == 0) || (number == 1))
>>          return number;
>>       else
>>          return fibonacci(number - 1) + fibonacci(number - 2);
>>    }
|  Added method fibonacci(long)

-> /methods
|    fibonacci (long)long

-> fibonacci( 12 )
|  Expression value is: 144
|    assigned to temporary variable $1 of type long

-> int[] array = { 1,2,3,4,5,6,7,8 };
|  Added variable array of type int[] with initial value [I@4f4a7090

-> for( long i : array ) { System.out.println(fibonacci( i )); }
1
1
2
3
5
8
13
21

In the same JShell session I can redefine the definition of the Fibonacci method and execute the same code. In this way you can use the REPL to quickly execute, modify, and test out new algorithms.

Listing 3. REPL for re-use


-> long fibonacci(long number) {
>>     return 1;
>> }
|  Modified method fibonacci(long)

-> for( long i : array ) { System.out.println(fibonacci( i )); }
1
1
1
1
1
1
1

Define a class

The following example demonstrates how to define an entire class in JShell and then reference that class in an expression -- all without leaving the REPL. The ability to dynamically create and test code frees you up to quickly experiment and iterate with new code.

Listing 4. Dynamic class definition


MacOSX:repl tobrien$ bash ./scripts/run.sh 
|  Welcome to JShell -- Version 0.710
|  Type /help for help

-> class Person {
>>     public String name;
>>     public int age;
>>     public String description;
>> 
>>     public Person( String name, int age, String description ) {
>>         this.name = name;
>>         this.age = age;
>>         this.description = description;
>>     }
>> 
>>     public String toString() {
>>         return this.name;
>>     }
>> }
|  Added class Person

-> Person p1 = new Person( "Tom", 4, "Likes Spiderman" );
|  Added variable p1 of type Person with initial value Tom

-> /vars
|    Person p1 = Tom

While the ability to define classes dynamically is powerful it isn't like developers are clamoring to write large, multi-line definitions in an interactive shell. This is where the concept of history and being about to load and save the state of the REPL starts to become important. With the /history command you can list all of the statements and expressions evaluated in a REPL.

Listing 5. Know your /history


-> /history

class Person {
    public String name;
    public int age;
    public String description;
    public Person( String name, int age, String description ) {
        this.name = name;
        this.age = age;
        this.description = description;
    }
    public String toString() {
        return this.name;
    }
}
Person p1 = new Person( "Tom", 4, "Likes Spiderman" );
Person p2 = new Person( "Zach", 10, "Good at Math" );
/vars
p1
p2
/history

You can then save your REPL history to a file and name it so that it can be loaded again later. Here's an example:


-> /save output.repl

-> /reset
|  Resetting state.

-> /vars

-> /open output.repl

-> /vars
|    Person p1 = Tom
|    Person p2 = Zach

The /save command saves the REPL history to a file, the /reset command resets the state of the REPL, and the /open command reads in a file and executes states against the REPL. The save and open features enable you to set up very complex REPL scripts that you can use to configure different REPL scenarios.

Editing class definition on the fly

JShell also makes it possible to set a startup definition file and load definitions automatically. You can jump around your REPL history and edit named source entries. For example, if I wanted to modify the definition of the Person class from this example I could use the /list and /edit commands.

Listing 6. Modifying Person


-> /l

   1 : class Person {
           public String name;
           public int age;
           public String description;
           public Person( String name, int age, String description ) {
               this.name = name;
               this.age = age;
               this.description = description;
           }
           public String toString() {
               return this.name;
           }
       }
   2 : Person p1 = new Person( "Tom", 4, "Likes Spiderman" );
   3 : Person p2 = new Person( "Zach", 10, "Good at Math" );
   4 : p1
   5 : p2

-> /edit 1

Running this /edit command loads a simple editor where I can alter the class definition and have the class updated immediately.

What's the big deal?

Talk to a Clojure or LISP programmer about how they develop day to day, and you'll see that they code within a REPL. They don't write scripts and then execute them as much as they spend most of their development time changing code interactively. If you have a few hours to spare, ask a Scala or Clojure developer about their REPL. It's how they work.

Java's a different language from Scala or Clojure. Java developers are not spending days focused on single lines of LISP that might contain entire program structures in a few statements. Most Java programs require setup to function properly, and while recent changes to the language have reduced the line count of systems written in Java we're still measuring the complexity of our systems in thousands of lines of code. The simple Person example listed above isn't useful code, and most useful code in Java carries with it a complexity that's going to be difficult to fit into a REPL-based programming environment.

Scala and Clojure developers practice something that Clojure Programming author Chas Emerick calls "iterative development" that doesn't depend on a file-based workflow. Java developers depend on tens of libraries, complex dependency hierarchies, and containers like Tomcat or TomEE. For this reason I don't predict that REPL-oriented programming will overtake traditional Java development in an IDE. Instead I see the Java REPL providing a few distinct benefits and opportunities.

1. Learning Java: Because Java programs require so much setup it can be challenging for developers learning the language to understand the syntax quickly. Java 9's REPL will become the primary way that new developers come to grips with the basic syntax.

2. Experimenting with new libraries: Java has hundreds of useful open source libraries for everything from date and time manipulation to math libraries. Without a REPL, whenever a developer wants to understand a new library they inevitably create a few throwaway classes with the usual "public static void main" ceremony. With a REPL you can just fire it up and play without this overhead.

3. Rapid prototyping: This is closer to how most Clojure and Scala developers work iteratively, but if you are working on a focused problem, a REPL makes it easy to iterate quickly on changes to classes and algorithms. With a REPL you won't have to wait for a build to finish; you can tweak the definition of a class quickly, reset your REPL, and try it again.

4. Integration with build systems: Gradle provides an interactive "shell" mode, and the Maven community have shipped similar tools in the past. Developers looking to reduce build complexity could explore using a REPL as a tool to orchestrate other systems.

My final 2c

I see the Java REPL as something that will start to affect day-to-day development over the next few years, for those who do upgrade to Java 9. I also think that the Java community is going to need time to fully adjust to the new development style and understand both the challenges and opportunities that a REPL provides. I don't expect that most Java developers will transition to REPL-oriented development like their Clojure programming cousins have, but I do think that we'll see REPL influence the way that new developers learn Java. As new Java developers encounter Java for the first time within a REPL, there's no doubt that it will start to influence how we build and prototype Java-based systems.

Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.