问题
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.
回答1:
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))
回答2:
I was looking at this recently and found Ammonite to be a sufficient solution for my needs.
- Add Ammonite to your library dependencies:
libraryDependencies += "com.lihaoyi" % "ammonite" % "1.6.0" cross CrossVersion.full
- Invoke Ammonite where you want to drop to the REPL shell:
ammonite.Main().run()
Note that you have to pass any variables you want bound inside of run, e.g. run("var1" -> var1)
. Have a look at their example - Instantiating Ammonite.
来源:https://stackoverflow.com/questions/24674288/clean-solution-for-dropping-into-repl-console-in-the-middle-of-program-execution