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

好久不见. 提交于 2019-12-03 12:29:52
lpiepiora

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))

I was looking at this recently and found Ammonite to be a sufficient solution for my needs.

  1. Add Ammonite to your library dependencies: libraryDependencies += "com.lihaoyi" % "ammonite" % "1.6.0" cross CrossVersion.full
  2. 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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!