Acceda al intérprete durante la ubicación arbitraria del código Scala

84

Vengo de un fondo de Python, donde en cualquier punto de mi código puedo agregar

import pdb; pdb.set_trace()

y en tiempo de ejecución me dejarán en un intérprete interactivo en ese lugar. ¿Existe un equivalente para scala o esto no es posible en tiempo de ejecución?

Lars Yencken
fuente
7
En el espíritu de la "verdad en la publicidad", Scala no tiene intérprete. Su REPL es "compilar y listo". Dicho esto, el código REPL (incluido el compilador) se puede incorporar a su aplicación, si lo desea (como se muestra a continuación)
Randall Schulz
1
Pero el REPL se iniciará sin ningún conocimiento de su contexto de ejecución, excepto lo que enlace explícita y laboriosamente en su código de lanzamiento de REPL. Vea abajo. Creo que en Python aterrizas en el contexto de ejecución, que es mucho mejor. de todos modos, stackoverflow.com/questions/24674288/… está más actualizado.
matanster

Respuestas:

78

Sí, puede hacerlo en Scala 2.8. Tenga en cuenta que, para que esto funcione, debe incluir scala-compiler.jar en su classpath. Si invoca su programa scala con scala, se hará automáticamente (o eso parece en las pruebas que hice).

Luego puede usarlo así:

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

Puede pasar varios DebugParamargumentos. Cuando aparezca el REPL, el valor de la derecha estará vinculado a un valor cuyo nombre proporcionó a la izquierda. Por ejemplo, si cambio esa línea así:

      breakIf(i == 5, DebugParam("j", i))

Entonces la ejecución sucederá así:

C:\Users\Daniel\Documents\Scala\Programas>scala TestDebugger
0
1
2
3
4
j: Int

scala> j
res0: Int = 5

Continúa la ejecución escribiendo :quit.

También puede caer incondicionalmente en REPL invocando break, que recibe un Listde en DebugParamlugar de un vararg. Aquí hay un ejemplo completo, código y ejecución:

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

Y entonces:

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 C. Sobral
fuente
3
Esto puede provocar un error scala.tools.nsc.MissingRequirementError: object scala not found.en Scala 2.8. Puede que tenga que pasar explícitamente la ruta de clase del proceso de acogida a los ajustes de los scalac, pero breaky breakIfqué no hacer esto. Aquí hay una versión parcheada de breakeso: gist.github.com/290632
retronym
@retronym Gracioso, simplemente funcionó aquí. Envíelo a paulp. Mencionó que esto iba a cambiar de todos modos.
Daniel C. Sobral
Lo probé desde una prueba JUnit, ejecutada por IntelliJ. IntelliJ lanzó el proceso con java -classpath .... Supongo que si lo usa scala -classpathen su lugar, funcionaría bien.
retrónimo
4
Era una dependencia del módulo y, por lo tanto, en el classpath. 2.8 no pasa el contenido del java -classpathproceso del host a la configuración de scalac: old.nabble.com/…
retronym
1
@Huur Ver respuesta de Răzvan Panda .
Daniel C. Sobral
24

Para agregar a la respuesta de Daniel, a partir de Scala 2.9, los métodos breaky breakIfestán contenidos en scala.tools.nsc.interpreter.ILoop. Además, DebugParames ahora NamedParam.

Kipton Barros
fuente
Deberá agregar jline como dependencia.
schmmd
8
¿Puede escribir un ejemplo con el nuevo uso?
Será el
24

IntelliJ IDEA:

  1. Ejecutar en modo de depuración o adjuntar un depurador remoto
  2. Establece un punto de interrupción y corre hasta alcanzarlo
  3. Abrir Evaluate Expression( Alt+F8 ventana , en el menú: Ejecutar -> Evaluar expresión) para ejecutar código Scala arbitrario.
  4. Escriba qué fragmento de código o expresión desea ejecutar y haga clic en Evaluar
  5. Escriba Alt+ Vo haga clic en Evaluar para ejecutar el fragmento de código.

Eclipse:

A partir de Scala 2.10, ambos breaky breakIfse han eliminado de ILoop.

Para entrar en intérprete tendrá que trabajar ILoopdirectamente con él .

Primero agregue la scala compilerbiblioteca. Para Eclipse Scala, haga clic derecho en proyecto => Build Path=> Add Libraries...=> Scala Compiler.

Y luego puede usar lo siguiente donde desee iniciar el intérprete:

import scala.tools.nsc.interpreter.ILoop
import scala.tools.nsc.interpreter.SimpleReader
import scala.tools.nsc.Settings

val repl = new ILoop
repl.settings = new Settings
repl.settings.Yreplsync.value = true
repl.in = SimpleReader()
repl.createInterpreter()

// bind any local variables that you want to have access to
repl.intp.bind("row", "Int", row)
repl.intp.bind("col", "Int", col)

// start the interpreter and then close it after you :quit
repl.loop()
repl.closeInterpreter()

En Eclipse Scala, el intérprete se puede utilizar desde la Consolevista:

Panda Răzvan Flavius
fuente
18
Esto es horrible. :(
Daniel C. Sobral
@Daniel ¿Por qué es horrible?
Hakkar
14
Porque agrega una gran cantidad de calderas que no tiene nada que ver con el objetivo de depurar en un punto del programa y, en cambio, está relacionado con la mecánica de poner en marcha el REPL.
Daniel C. Sobral
1
@Daniel, ¿hay alguna forma mejor en scala 2.10?
Roterl
@roterl ¿Cuál es el problema con lo anterior?
Daniel C. Sobral