A REPL (read-evaluate-print loop) is an integral part of dynamic programming languages such as Lisp or Python. It's a great tool because you can experiment with language and library features without having to write complete programs. If all goes according to plan, Java 9 will have its own REPL. In this blog, I show you how to build the pre-release version and play with it.
One of the joys of programming with a dynamic language such as Lisp or Python is the instant feedback you get from the interpreter. You try out a thing or two, something starts working, you move on to the next issue, and before you know it, your task is done. Technically, the program that reads and evaluates the language statements is called a REPL, for “read-evaluate-print loop”.
And it's not just dynamic languages. Scala is statically typed, and it comes with a REPL. Behind the scenes, the statements that you enter are compiled and executed.
(As a potentially entertaining aside, make a Google image search for REPL. Perhaps because I am on sabbatical in Switzerland, I get an ad for “Hypower Musli capsules”.)
Why can't we get a REPL with Java? Good question. There are some ways of coming close. Way back when, BeanShell was a plausible solution, but it never caught up to Java 5. Some educational environments, such as BlueJ and Doctor Javahave perfectly reasonable facilities for interactively evaluating Java code. But somehow, even people who use and love these environments don't seem to use that feature a lot.
So, after more than twenty years, Java is finally going to do it right in Java 9. For now, you still have to build the REPL by hand—it's not yet in the binary distribution. But it's not hard to do.
These instructions should get you going on Mac OS X. But if you use Linux, your life is even simpler. (I haven't found any instructions for Windows, and in general, the Windows build process for the JDK seems fiddly. If you run Windows and want to check this out, just use a Linux VM.) Here goes:
hg clone http://hg.openjdk.java.net/kulla/dev jshell cd jshell wget http://repo1.maven.org/maven2/jline/jline/2.12/jline-2.12.jar sh get_source.sh export JAVA_HOME=~/jdk1.9.0 export PATH=$JAVA_HOME/bin:$PATH export JLINE2LIB=~/jshell/jline-2.12.jar cd langtools/repl sh scripts/compile.sh
export JAVA_HOME=~/jdk1.9.0/ export PATH=$JAVA_HOME/bin:$PATH export JLINE2LIB=~/jshell/jline-2.12.jar cd ~/jshell/langtools/repl sh scripts/run.sh
Now you are ready to roll. Let's say you want to run some experiments with streams. First import some packages.
import java.util.stream.*; import java.nio.file.*;
Let's read all words from
/usr/share/dict/words into a stream:
-> Files.lines(Paths.get("/usr/share/dict/words")) | Expression value is: java.util.stream.ReferencePipeline$Head@6108b2d7 | assigned to temporary variable $1 of type java.util.stream.Stream<String>
As you can see, this is a rather verbose REPL, but maybe that's not a bad thing for users who aren't accustomed to one.
You can now work with the temporary variable
$1 and process it further:
$1.filter(w -> w.length() > 20) | Expression value is: java.util.stream.ReferencePipeline$2@180bc464 | assigned to temporary variable $2 of type Stream<String>
Why not make an explicit variable? You can, but then you have to know its type:
Stream<String> res = Files.lines(Paths.get("/usr/share/dict/words"))
In a dynamically typed language, where variables don't have types, this issue doesn't arise. And in Scala, types are inferred for variables, so you don't have to worry about declaring their types either. But in the Java REPL, you are likely to use the $n variables a lot.
So, to complete our example, you might type
Then you can hit the TAB key, and you get autocompletion suggestions:
Collection Collections Collector Collectors
When you complete to
Collectors.to, you get more suggestions:
toCollection( toConcurrentMap( toList() toMap( toSet()
) to get
$2.collect(Collectors.toList()) | Expression value is: [Andrianampoinimerina's, counterintelligence's, counterrevolutionaries, counterrevolutionary's, electroencephalogram's, electroencephalograms, electroencephalograph, electroencephalograph's, electroencephalographs] | assigned to temporary variable $3 of type List<String>
Now we can be bolder and try it all at once, this time yielding an array:
Files.lines(Paths.get("/usr/share/dict/words")).filter(w -> w.length() > 20).toArray() | Expression value is: [Ljava.lang.Object;@153f5a29 | assigned to temporary variable $4 of type Object
Ok, that's interesting—we get the Java weirdness that arrays inherit a useless
toString method. It's easy enough to recover:
Arrays.toString($4) | Expression value is: "[Andrianampoinimerina's, counterintelligence's, counterrevolutionaries, counterrevolutionary's, electroencephalogram's, electroencephalograms, electroencephalograph, electroencephalograph's, electroencephalographs]" | assigned to temporary variable $5 of type String
Maybe they could relent and print arrays for us.
Also. it's a bit of a pain that one needs to pay close attention to the numbers with the temporary variables. Maybe
$0 could refer to the last result?
Overall, the REPL is quite nice, but with a bit of polish, it could be even nicer. These are early days, so there is hope.
(As an aside, try a Google image search for hope.)
Check it out and let them know what you think! There is some documentation here, and the mailing list is here.