88

I come from a Python background, where at any point in my code I can add

import pdb; pdb.set_trace()

and at runtime I'll be dropped into an interactive interpreter at that spot. Is there an equivalent for scala, or is this not possible at runtime?

Lars Yencken
  • 2,976
  • 2
  • 21
  • 12
  • 7
    In the spirit of "truth in advertising," Scala has no interpreter. Its REPL is "compile-and-go." That said, the REPL code (including the compiler) can be incorporated into your application, if you wish (as shown below) – Randall Schulz Jan 29 '10 at 15:41
  • 1
    But the REPL will launch without any knowledge of your running context except for what you explicitly and laboriously bind in your REPL-launching code. See below. I think in python you land into the running context which is much better. anyway, http://stackoverflow.com/questions/24674288/clean-solution-for-dropping-into-repl-console-in-the-middle-of-program-execution?lq=1 is more up-to-date. – matanster Sep 06 '14 at 17:11

3 Answers3

79

Yes, you can, on Scala 2.8. Note that, for this to work, you have to include the scala-compiler.jar in your classpath. If you invoke your scala program with scala, it will be done automatically (or so it seems in the tests I made).

You can then use it like this:

import scala.tools.nsc.Interpreter._

object TestDebugger {
  def main(args: Array[String]) {
    0 to 10 foreach { i =>
      breakIf(i == 5, DebugParam("i", i))
      println(i)
    }
  }
}

You may pass multiple DebugParam arguments. When the REPL comes up, the value on the right will be bound to a val whose name you provided on the left. For instance, if I change that line like this:

      breakIf(i == 5, DebugParam("j", i))

Then the execution will happen like this:

C:\Users\Daniel\Documents\Scala\Programas>scala TestDebugger
0
1
2
3
4
j: Int

scala> j
res0: Int = 5

You continue the execution by typing :quit.

You may also unconditionally drop into REPL by invoking break, which receives a List of DebugParam instead of a vararg. Here's a full example, code and execution:

import scala.tools.nsc.Interpreter._

object TestDebugger {
  def main(args: Array[String]) {
    0 to 10 foreach { i =>
      breakIf(i == 5, DebugParam("j", i))
      println(i)
      if (i == 7) break(Nil)
    }
  }
}

And then:

C:\Users\Daniel\Documents\Scala\Programas>scalac TestDebugger.scala

C:\Users\Daniel\Documents\Scala\Programas>scala TestDebugger
0
1
2
3
4
j: Int

scala> j
res0: Int = 5

scala> :quit
5
6
7

scala> j
<console>:5: error: not found: value j
       j
       ^

scala> :quit
8
9
10

C:\Users\Daniel\Documents\Scala\Programas>
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 3
    This may lead to an error `scala.tools.nsc.MissingRequirementError: object scala not found.` in Scala 2.8. You may need to explicitly pass the classpath of the host process to the Settings of Scalac, but `break` and `breakIf` don't do this. Here's a patched version of `break` that does: http://gist.github.com/290632 – retronym Jan 30 '10 at 17:14
  • @retronym Funny, it just worked here. Send it to paulp. He mentioned this thing was going to be changed anyway. – Daniel C. Sobral Jan 30 '10 at 18:37
  • I tried it from a JUnit test, run by IntelliJ. IntelliJ launched the process with `java -classpath ...`. I guess if you use `scala -classpath` instead it would work fine. – retronym Jan 30 '10 at 18:48
  • @retronym If you are launching it from JUnit, then you have to add the scala-compiler.jar to the classes in the library of the project structure, I suppose. I said you needed to add it to the classpath. – Daniel C. Sobral Jan 31 '10 at 20:15
  • 4
    It was a dependency of the module, and hence in the classpath. 2.8 doesn't pass the contents of `java -classpath` of the host process to the settings for scalac: http://old.nabble.com/-scala--recent-changes-in-2.8-nightly-classpath-management-td26233977.html – retronym Feb 01 '10 at 06:27
  • This is exactly what I had in mind, but it doesn't compile for me in Scala 2.7.5final. Is this new to Scala 2.8? TestDebugger.scala:6: error: not found: value breakIf breakIf(i == 5, DebugParam("j", i)) ^ one error found – Lars Yencken Feb 02 '10 at 05:26
  • @Lars Yes, it is only available on Scala 2.8. Sorry for not indicating that in the answer. – Daniel C. Sobral Feb 02 '10 at 10:53
  • I am running the example in Scala 2.8 but I get the following exception. Do you have any idea on how could I fix that? Exception in thread "main" java.lang.NoClassDefFoundError: scala/io/LowPriorityCodecImplicits – mariosangiorgio Jul 30 '10 at 14:27
  • Actually the issue is with Eclipse, running it from the command line makes it perform as desired. – mariosangiorgio Jul 30 '10 at 15:32
  • I still get the error @retronym mentioned a long time ago. I'm using 2.9.1. – schmmd Dec 09 '11 at 19:04
  • I get the error with `mvn exec:java` or `java -cp`. I don't get the error when running a fat jar with `scala -cp`. – schmmd Dec 09 '11 at 19:19
  • Strange, I think I used to have this working, but it's now hanging for me on Scala 2.9.1 – Kipton Barros Jan 09 '12 at 01:42
  • What about for Scala 2.10 and above (scala.reflect.universe._ was added)? – Hakkar Oct 26 '13 at 05:10
  • 1
    @Huur See [answer](http://stackoverflow.com/a/19070053/53013) by [Răzvan Panda](http://stackoverflow.com/users/750216/rzvan-panda). – Daniel C. Sobral Oct 28 '13 at 17:51
  • Thanks Daniel! Sorry, for not noticing that. I'll check it out later tonight. – Hakkar Oct 28 '13 at 18:59
25

IntelliJ IDEA:

  1. Run in debug mode or attach a remote debugger
  2. Set a breakpoint and run until you reach it
  3. Open Evaluate Expression (Alt+F8, in menu: Run -> Evaluate Expression) window to run arbitrary Scala code.
  4. Type what code fragment or expression you want to run and click on Evaluate
  5. Type Alt+V or click on Evaluate to run the code fragment.

Eclipse:

As of Scala 2.10 both break and breakIf have been removed from ILoop.

To break into interpreter you will have to work with ILoop directly.

First add scala compiler library. For Eclipse Scala, right click on project => Build Path => Add Libraries... => Scala Compiler.

And then you can use the following where you want to start the interpreter:

import scala.tools.nsc.interpreter.ILoop
import scala.tools.nsc.interpreter.SimpleReader
import scala.tools.nsc.Settings

val repl = new ILoop
repl.settings = new Settings
repl.settings.Yreplsync.value = true
repl.in = SimpleReader()
repl.createInterpreter()

// bind any local variables that you want to have access to
repl.intp.bind("row", "Int", row)
repl.intp.bind("col", "Int", col)

// start the interpreter and then close it after you :quit
repl.loop()
repl.closeInterpreter()

In Eclipse Scala the interpreter can be used from the Console view:

Răzvan Flavius Panda
  • 21,730
  • 17
  • 111
  • 169
  • @Daniel Why is that horrible? – Hakkar Oct 28 '13 at 19:08
  • 14
    Because it adds a lot of boiler-plate which is completely unrelated to the goal of debugging at a point in the program, and, instead, related to the mechanics of getting the REPL going. – Daniel C. Sobral Oct 29 '13 at 00:48
  • 1
    @Daniel is there any better way in scala 2.10 ? – roterl Jun 08 '14 at 14:51
  • @roterl What is the problem with the above? – Daniel C. Sobral Jun 09 '14 at 02:15
  • @Daniel you wrote "That's horrible. :( " with explanation why. I agree with that. But is there a better way? – roterl Jun 09 '14 at 06:56
  • @roterl I really don't know. I assume there isn't. I said it was horrible because, well, it was really neat before, not because I thought there was a better way. – Daniel C. Sobral Jun 09 '14 at 18:26
  • In Scala 2.11.6 this produces a runtime error `object scala in compiler mirror not found.` – devth Mar 17 '15 at 22:04
  • @DanielC.Sobral I updated the answer to be less terrible (for IntelliJ IDEA at least) :) – Răzvan Flavius Panda Mar 29 '16 at 14:13
  • This could be used to create a REPL debugger that could be included in a Scala project, much like Pry. I have a feeling the explicit 'break' functionality was removed because the core contributors of the Scala languages felt having a built-in way of dropping into a REPL to debug Scala was less flexible and would stunt innovation on better debugging tools (and it's really also scope-creep for designing the language itself). – josiah May 26 '17 at 03:26
25

To add to Daniel's answer, as of Scala 2.9, the break and breakIf methods are contained in scala.tools.nsc.interpreter.ILoop. Also, DebugParam is now NamedParam.

Kipton Barros
  • 21,002
  • 4
  • 67
  • 80