views:

267

answers:

2

I almost always have a Scala REPL session or two open, which makes it very easy to give Java or Scala classes a quick test. But if I change a class and recompile it, the REPL continues with the old one loaded. Is there a way to get it to reload the class, rather than having to restart the REPL?

Just to give a concrete example, suppose we have the file Test.scala:

object Test { def hello = "Hello World" }

We compile it and start the REPL:

~/pkg/scala-2.8.0.Beta1-prerelease$ bin/scala
Welcome to Scala version 2.8.0.Beta1-prerelease
(Java HotSpot(TM) Server VM, Java 1.6.0_16).
Type in expressions to have them evaluated.
Type :help for more information.

scala> Test.hello
res0: java.lang.String = Hello World

Then we change the source file to

object Test {
  def hello = "Hello World"
  def goodbye = "Goodbye, Cruel World"
}

but we can't use it:

scala> Test.goodbye
<console>:5: error: value goodbye is not a member of object Test
       Test.goodbye
            ^

scala> import Test;
<console>:1: error: '.' expected but ';' found.
       import Test;
+5  A: 

Class reloading is not an easy problem. In fact, it's something that the JVM makes very difficult. You do have a couple options though:

  • Start the Scala REPL in debug mode. The JVM debugger has some built-in reloading which works on the method level. It won't help you with the case you gave, but it would handle something simple like changing a method implementation.
  • Use JRebel (http://www.zeroturnaround.com/jrebel). JRebel is basically a super-charged class reloading solution for the JVM. It can handle member addition/removal, new/removed classes, definition changes, etc. Just about the only thing it can't handle is changes in class hierarchy (adding a super-interface, for example). It's not a free tool, but they do offer a complementary license which is limited to Scala compilation units.

Unfortunately, both of these are limited by the Scala REPL's implementation details. I use JRebel, and it usually does the trick, but there are still cases where the REPL will not reflect the reloaded class(es). Still, it's better than nothing.

Daniel Spiewak
+2  A: 

There is an alternative to reloading the class if the goal is to not have to repeat previous commands. The REPL has the command

:replay

which restarts the REPL environment and plays back all previous valid commands. (The invalid ones are skipped, so if it was wrong before, it won't suddenly work.) When the REPL is reset, it does reload classes, so new commands can use the contents of recompiled classes (in fact, the old commands will also use those recompiled classes).

This is not a general solution, but is a useful shortcut to extend an individual session with re-computable state.

Rex Kerr