views:

665

answers:

1

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?

+12  A: 

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
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
@retronym Funny, it just worked here. Send it to paulp. He mentioned this thing was going to be changed anyway.
Daniel
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
@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
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
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
@Lars Yes, it is only available on Scala 2.8. Sorry for not indicating that in the answer.
Daniel
@Daniel Bingo, got it working. Many thanks!
Lars Yencken
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
Actually the issue is with Eclipse, running it from the command line makes it perform as desired.
mariosangiorgio