Clean solution for dropping into REPL console in the middle of program execution

sbt, scala, scala-2.10

Solution

You could easily reimplement the `breakIf` method in your code. I don't think there is much cleaner way of doing that.

First you have to add a scala compiler library to your `build.sbt`

libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value

Once that's done you can implement `breakIf`

import scala.reflect.ClassTag
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.{ILoop, NamedParam}

def breakIf[T](assertion: => Boolean, args: NamedParam*)(implicit tag: ClassTag[T]) = {
    val repl = new ILoop()

    repl.settings = new Settings()
    repl.settings.embeddedDefaults[T]
    repl.settings.Yreplsync.value = true
    repl.in = repl.chooseReader(repl.settings)

    repl.createInterpreter()

    args.foreach(p => repl.bind(p.name, p.tpe, p.value))

    repl.loop()
    repl.closeInterpreter()
  }

I think it's pretty straight forward, the only tricky part is that you have to set-up the classpath properly. You need to call `embeddedDefaults` with a class from your project (see my answer to another question).

You can use the new `breakIf` as follows:

val x = 10
breakIf[X](assertion = true, NamedParam("x", "Int", x))

Where `X` is just some of your classes.

I don't know if this answers your question, because it's hard to measure what is easy and what is hard.

Additionally just as a side note - if you want to use it for debugging purposes, why not use a debugger. I guess most of the debuggers can connect to a program, stop at a breakpoint and evaluate expressions in that context.

Edit

Seems like it doesn't work on current release of Scala 2.10, the working code seems to be:

import scala.reflect.ClassTag
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.{ILoop, NamedParam}

def breakIf[T](assertion: => Boolean, args: NamedParam*)(implicit tag: ClassTag[T]) = {

  val repl = new ILoop() {
    override protected def postInitialization(): Unit = {
      addThunk(args.foreach(p => intp.bind(p)))
      super.postInitialization()
    }
  }

  val settings = new Settings()

  settings.Yreplsync.value = true
  settings.usejavacp.value = true
  settings.embeddedDefaults[T]

  args.foreach(repl.intp.rebind)

  repl.process(settings)

}

and usage is like

  val x = 10
  breakIf[X](assertion = true, NamedParam("x", x))

Problem

Is there any working solution for dropping into REPL console with for Scala 2.10? This is mainly for debugging purpose - I want to pause in the middle of execution, and have a REPL console where I can inspect values and test the program's logic using complex expressions within my program at the current state of execution. Those who have programmed in Ruby might know similar function: the `binding.pry`. AFAIK, Scala 2.9 and under used to have `breakIf` but it has been removed from the later versions. Using `ILoop` seems to be the new way but introduced issues due to `sbt` not adding scala-library to the classpath. Several solutions such as this and this seem to offer a good workaround but my point is there must be a solution where I don't have to spend hours or even days just to make the REPL working. In short, there's a lot more boilerplate steps involved - this is in contrast with `binding.pry` which is just a line of code with no additional boilerplate. I am not aware if there's an issue introduced in executing the program as an sbt task as opposed to if running the program executable directly, but for development purpose I am currently running and testing my program using sbt task.

Original source

Related problems