清洁解决方案,用于在程序执行过程中放入REPL控制台

时间:2014-07-10 10:32:37

标签: scala sbt scala-2.10

是否有任何解决方案可以使用Scala 2.10进入REPL控制台?

这主要用于调试目的 - 我想在执行过程中暂停,并且有一个REPL控制台,我可以检查值并使用当前执行状态下程序中的复杂表达式测试程序的逻辑。那些使用Ruby编程的人可能知道类似的功能:binding.pry

AFAIK,Scala 2.9及以下用于breakIf,但已从更高版本中删除。使用ILoop似乎是一种新方法,但由于sbt没有将scala-library添加到类路径而引入了问题。

thisthis等几种解决方案似乎提供了一个很好的解决方法,但我的观点是必须有一个解决方案,我不必花费数小时甚至数天来制作REPL工作

简而言之,涉及更多的样板步骤 - 这与binding.pry形成鲜明对比,{{1}}只是一行代码而没有额外的样板。

我不知道在执行程序作为sbt任务时是否存在问题,而不是直接运行程序可执行文件,但出于开发目的,我正在运行并使用sbt任务测试我的程序。

2 个答案:

答案 0 :(得分:5)

您可以轻松地在代码中重新实现breakIf方法。我认为没有更清洁的方法。

首先,您必须向build.sbt

添加一个scala编译器库
libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value

完成后,您可以实施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()
  }

我认为这很简单,唯一棘手的部分是你必须正确设置类路径。您需要使用项目中的课程致电embeddedDefaults(请参阅我对another question的回答)。

您可以按如下方式使用新的breakIf

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

X只是你的一些课程。

我不知道这是否能回答你的问题,因为很难衡量什么是 easy 和什么是难的。

另外,作为旁注 - 如果您想将其用于调试目的,为什么不使用调试器。我猜大多数调试器都可以连接到程序,在断点处停止并在该上下文中计算表达式。

修改

似乎它在当前版本的Scala 2.10上不起作用,工作代码似乎是:

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)

}

和用法就像

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

答案 1 :(得分:0)

我最近正在研究这个问题,发现Ammonite是满足我需要的足够解决方案。

  1. 将Ammonite添加到您的库依赖项中: libraryDependencies += "com.lihaoyi" % "ammonite" % "1.6.0" cross CrossVersion.full
  2. 在要放入REPL shell的地方调用Ammonite: ammonite.Main().run()

请注意,您必须在运行时传递要绑定的所有变量,例如run("var1" -> var1)。看看他们的示例-Instantiating Ammonite