Entrare nell'interprete durante la posizione arbitraria del codice scala


85

Vengo da un background Python, dove in qualsiasi punto del mio codice posso aggiungere

import pdb; pdb.set_trace()

e in fase di esecuzione verrò inserito in un interprete interattivo in quel punto. Esiste un equivalente per scala o non è possibile in fase di esecuzione?


7
Nello spirito della "verità nella pubblicità", Scala non ha interpreti. Il suo REPL è "compile-and-go". Detto questo, il codice REPL (incluso il compilatore) può essere incorporato nella tua applicazione, se lo desideri (come mostrato di seguito)
Randall Schulz,

1
Ma REPL verrà avviato senza alcuna conoscenza del contesto in cui si esegue, tranne per ciò che si lega in modo esplicito e laborioso nel codice di avvio di REPL. Vedi sotto. Penso che in Python si atterra nel contesto di esecuzione che è molto meglio. in ogni caso, stackoverflow.com/questions/24674288/… è più aggiornato.
matanster

Risposte:


78

Sì, puoi, su Scala 2.8. Nota che, affinché funzioni, devi includere scala-compiler.jar nel tuo classpath. Se invocate il vostro programma scala con scala, sarà fatto automaticamente (o almeno così sembra nei test che ho fatto).

Puoi quindi usarlo in questo modo:

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

Puoi passare più DebugParamargomenti. Quando viene visualizzato il REPL, il valore a destra sarà associato a un val di cui hai fornito il nome a sinistra. Ad esempio, se cambio quella riga in questo modo:

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

Quindi l'esecuzione avverrà in questo modo:

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

scala> j
res0: Int = 5

Continui l'esecuzione digitando :quit.

Puoi anche entrare incondizionatamente in REPL invocando break, che riceve un Listdi DebugParaminvece di un vararg. Ecco un esempio completo, codice ed esecuzione:

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

E poi:

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>

3
Ciò potrebbe causare un errore scala.tools.nsc.MissingRequirementError: object scala not found.in Scala 2.8. Potrebbe essere necessario passare esplicitamente classpath del processo host per le impostazioni di scalac, ma breake breakIfnon fare questo. Ecco una versione con patch di breakche fa: gist.github.com/290632
retronym

@retronym Divertente, ha funzionato qui. Mandalo a paulp. Ha detto che questa cosa sarebbe cambiata comunque.
Daniel C. Sobral

L'ho provato da un test JUnit, gestito da IntelliJ. IntelliJ ha avviato il processo con java -classpath .... Immagino che se lo usi scala -classpathinvece funzionerebbe bene.
retronym

4
Era una dipendenza del modulo e quindi nel classpath. 2.8 non trasferisce il contenuto del java -classpathprocesso host alle impostazioni di scalac: old.nabble.com/…
retronym


24

Per aggiungere alla risposta di Daniel, a partire da Scala 2.9, i metodi breake breakIfsono contenuti in scala.tools.nsc.interpreter.ILoop. Inoltre, DebugParamè adesso NamedParam.


Dovrai aggiungere jline come dipendenza.
schmmd

8
puoi scrivere un esempio con il nuovo utilizzo?
Il

24

IDEA IntelliJ:

  1. Esegui in modalità debug o collega un debugger remoto
  2. Imposta un punto di interruzione e corri finché non lo raggiungi
  3. Apri Evaluate Expression( Alt+F8 , nel menu: Esegui -> Valuta espressione) per eseguire codice Scala arbitrario.
  4. Digitare il frammento di codice o l'espressione che si desidera eseguire e fare clic su Valuta
  5. Digita Alt+ Vo fai clic su Valuta per eseguire il frammento di codice.

Eclisse:

A partire da Scala 2.10 entrambi breake breakIfsono stati rimossi da ILoop.

Per entrare nell'interprete dovrai lavorare ILoopdirettamente.

Per prima cosa aggiungi la scala compilerlibreria. Per Eclipse Scala, fare clic con il pulsante destro del mouse su progetto => Build Path=> Add Libraries...=> Scala Compiler.

E poi puoi usare quanto segue dove vuoi avviare l'interprete:

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

In Eclipse Scala è possibile utilizzare l'interprete dalla Consolevista:


@Daniel Perché è orribile?
Hakkar

14
Perché aggiunge un sacco di boilerplate che è completamente estraneo all'obiettivo del debug in un punto del programma e, invece, è correlato ai meccanismi di avvio del REPL.
Daniel C. Sobral,

1
@Daniel c'è un modo migliore in scala 2.10?
roterl

@roterl Qual è il problema con quanto sopra?
Daniel C. Sobral

@Daniel hai scritto "È orribile. :(" spiegando il perché. Sono d'accordo. Ma c'è un modo migliore?
roterl
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.