Is it possible to create (enter) nested environments in Scala REPL, such that after exiting the nested environment, all variable bindings created within the exited environment will be lost?
Here is what I wish a session could look like:
scala> val x = 1
x: Int = 1
scala> enter // How to implement this?
// Entering nested context (type exit to exit)
scala> val x = 2
x: Int = 2
scala> val y = 3
y: Int = 3
scala> exit // How to implement this?
// Exiting nested context
scala> assert(x == 1)
scala> y
<console>:12: error: not found: value y
y
^
scala>
This isn't possible with the current Scala REPL, but you can achieve something similar using the Ammonite REPL:
Welcome to the Ammonite Repl 0.8.2
(Scala 2.12.1 Java 1.8.0_121)
# val x = 1
x: Int = 1
# repl.sess.save("first")
res1_1: ammonite.repl.SessionChanged =
# val x = 2
x: Int = 2
# val y = 3
y: Int = 3
# repl.sess.save("second") ; repl.sess.load("first")
res4_1: ammonite.repl.SessionChanged =
Removed Imports: Set('y, 'res1_1, 'res1_0)
# y
cmd5.sc:1: not found: value y
val res5 = y
^
Compilation Failed
# x
res5: Int = 1
These sessions aren't nested exactly the way you describe, but are easy to track by name, and can overlap. That is after repl.sess.save("first"), you still have access to the original x if you don't override it.
After playing around with it some more, I was able to concoct a simple object that uses a stack to track the sessions and load/save them. It can be placed in ~/.ammonite/predef.sc to load automatically with the Ammonite REPL:
object SessionStack {
case class AmmSession(id: Int = 1) {
def name = s"session_${id}"
def next = AmmSession(id + 1)
}
private var sessions = collection.mutable.Stack.empty[AmmSession]
private var current = AmmSession()
def enter: Unit = {
sessions.push(current.copy())
repl.sess.save(current.name)
current = current.next
}
def exit: Unit = if(sessions.nonEmpty) {
current = sessions.pop()
repl.sess.load(current.name)
} else {
println("Nothing to exit.")
}
}
import SessionStack._
I haven't tested this rigorously, so there may be an edge-case that isn't covered, but I was able to go a few levels deep easily and then peel back the layers.
Related
What is the best way to inject a snippet of code to scala? something like eval in javascript and GroovyScriptEngine. I want to keep my rules/computations/formulas outside the actual data processing class. I have close to 100+ formulas to be executed. The data flow is same for all only the formulas change. What is the best way to do it in scala? and the number of formulas will grow over time.
You could use either scala-lang API for that or twitter-eval. Here is the snippet of a simple use case of scala-lang
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.IMain
object ScalaReflectEvaluator {
def evaluate() = {
val clazz = prepareClass
val settings = new Settings
settings.usejavacp.value = true
settings.deprecation.value = true
val eval = new IMain(settings)
val evaluated = eval.interpret(clazz)
val res = eval.valueOfTerm("res0").get.asInstanceOf[Int]
println(res) //yields 9
}
private def prepareClass: String = {
s"""
|val x = 4
|val y = 5
|x + y
|""".stripMargin
}
}
or with twitter:
import com.twitter.util.Eval
object TwitterUtilEvaluator {
def evaluate() = {
val clazz = prepareClass
val eval = new Eval
eval.apply[Int](clazz)
}
private def prepareClass: String = {
s"""
|val x = 4
|val y = 5
|x + y
|""".stripMargin
}
}
I am not able to compile it at the moment to check whether I have missed something but you should get the idea.
I've found that scala.tools.reflect.ToolBox is the fastest eval in scala (measured interpreter, twitter's eval and custom tool). It's API:
import scala.reflect.runtime.universe
import scala.tools.reflect.ToolBox
val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
tb.eval(tb.parse("""println("hello!")"""))
result.map { res =>
val totaldocs: Int = res.value
// do something with this number
}
//val totaldocs = 60
val totalpages:Int = (totaldocs/ipp)+1
Compilation error not found: value x but I declared it already, what is wrong with my implementation, sorry I am new to play framework and scala programming language.
I would say this line is the problem:
val totalpages:Int = (totaldocs/ipp)+1
because totaldocs is only defined inside the map scope
maybe you want something like:
private def getTotalPages(query:BSONDocument, ipp:Int) (implicit ec: ExecutionContext) = {
val key = collectionName + ":" + BSONDocument.pretty(query)
Logger.debug("Query key = "+key)
val command = Count(query)
val result: Future[CountResult] = collection.runCommand(command)
result.map { res =>
val totaldocs: Int = res.value
// do something with this number
val totalpages:Int = (totaldocs/ipp)+1
Logger.debug(s"Total docs $totaldocs, Total pages $totalpages, Items per page, $ipp")
totalpages
}
}
but now it will return a Future[Int] and you will have to deal with the future on the caller.
Note: this is just one solution, depending on your code it may not be the most adequate one
I have two statements like this:
val a = 1
val b = 2
In between the 2 statements, I want to pause for N seconds like I can in bash with sleep command.
You can try:
val a = 1
Thread.sleep(1000) // wait for 1000 millisecond
val b = 2
You can change 1000 to other values to accommodate to your needs.
Given:
package object wrap {
import java.time._
def delayed[A](a: => A): A = {
Console println Instant.now
Thread.sleep(1000L)
val x = a
Console println Instant.now
x
}
}
You can:
Welcome to Scala 2.12.0-M3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions for evaluation. Or try :help.
scala> $intp.setExecutionWrapper("wrap.delayed")
scala> { println("running"); 42 }
2016-02-20T06:28:17.372Z
running
2016-02-20T06:28:18.388Z
res1: Int = 42
scala> :quit
I am trying to get the code completion for a Scala interpreter to work. Ideally it would work identically to the one provided by the REPL (ILoop). I am using a text document as source, so I do not want to instantiate an ILoop but just IMain.
In the following example, the completion only works for special cases:
import scala.tools.nsc.interpreter.{JLineCompletion, IMain}
import scala.tools.nsc.Settings
object CompletionTest extends App {
val settings = new Settings
settings.usejavacp.tryToSetFromPropertyValue("true")
val intp = new IMain(settings)
intp.initializeSynchronous()
assert(intp.isInitializeComplete)
val comp = new JLineCompletion(intp)
val completer = comp.completer()
val buffer = "val x = Indexe"
val choices = completer.complete(buffer, buffer.length)
println("----BEGIN COMPLETION----")
choices.candidates.foreach(println)
println("----END COMPLETION----")
intp.close()
}
The expected output would be IndexedSeq, but it is empty. If I set the buffer to just Indexe, it works. If I set the buffer to Indexe (leading whitespace), the completion candidates are empty again.
So there must be an additional step involved in processing the buffer or invoking the completion. What exactly happens when <tab> is pressed in the REPL? It seems almost impossible to figure out which methods are called...
In JLineReader, you can see the wiring. JLineConsoleReader sets up an ArgumentCompleter with the ScalaCompleter as the underlying completer.
So the completer wants just the argument, not the line.
apm#mara:~$ scalam
Welcome to Scala version 2.11.0-M7 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'. **
** scala.tools.nsc._ has been imported **
** global._, definitions._ also imported **
** Try :help, :vals, power.<tab> **
scala> val b = "Indexe"
b: String = Indexe
scala> completion.completer complete (b, b.length)
res0: scala.tools.nsc.interpreter.Completion.Candidates = Candidates(0,List(IndexedSeq))
In other keystrokes,
// paste your code
scala> val buffer = "Indexe"
buffer: String = Indexe
scala> completer.complete(buffer, buffer.length)
res6: scala.tools.nsc.interpreter.Completion.Candidates = Candidates(0,List(IndexedSeq))
scala> import tools.nsc.interpreter.Completion.Candidates
import tools.nsc.interpreter.Completion.Candidates
scala> val Candidates(_, choices) = completer.complete(buffer, buffer.length)
choices: List[String] = List(IndexedSeq)
scala> choices foreach println
IndexedSeq
To hand it the full line:
scala> val argCompletor: ArgumentCompleter =new ArgumentCompleter(new JLineDelimiter, scalaToJline(comp.completer))
argCompletor: jline.console.completer.ArgumentCompleter = jline.console.completer.ArgumentCompleter#751222c7
scala> val maybes = new java.util.ArrayList[CharSequence]
maybes: java.util.ArrayList[CharSequence] = []
scala> val buffer = "val x = Indexe"
buffer: String = val x = Indexe
scala> argCompletor.setStrict(false)
scala> argCompletor.complete(buffer, buffer.length, maybes)
res32: Int = 8
scala> maybes
res33: java.util.ArrayList[CharSequence] = [IndexedSeq]
The delimiter does the line parse.
Edit - some value-added analysis:
"Strict" mode for the completor exists because you can supply a completor for each token on the line, and require each previous argument to be completable. For n completors, all args after nth arg are handled by the last completor.
Partial answer. I managed to drill a hole in that monster by overriding scalaToJline in JLineReader. That method is invoked with a pre-massaged string, following this trace:
at CompletionTest$$anon$1$$anon$2$$anon$3.complete(CompletionTest.scala:37)
at scala.tools.jline.console.completer.ArgumentCompleter.complete(ArgumentCompleter.java:150)
at scala.tools.jline.console.ConsoleReader.complete(ConsoleReader.java:1543)
at scala.tools.jline.console.ConsoleReader.readLine(ConsoleReader.java:1312)
at scala.tools.jline.console.ConsoleReader.readLine(ConsoleReader.java:1170)
at scala.tools.nsc.interpreter.JLineReader.readOneLine(JLineReader.scala:74)
at scala.tools.nsc.interpreter.InteractiveReader$$anonfun$readLine$2.apply(InteractiveReader.scala:42)
at scala.tools.nsc.interpreter.InteractiveReader$$anonfun$readLine$2.apply(InteractiveReader.scala:42)
at scala.tools.nsc.interpreter.InteractiveReader$.restartSysCalls(InteractiveReader.scala:49)
at scala.tools.nsc.interpreter.InteractiveReader$class.readLine(InteractiveReader.scala:42)
at scala.tools.nsc.interpreter.JLineReader.readLine(JLineReader.scala:19)
at scala.tools.nsc.interpreter.ILoop.readOneLine$1(ILoop.scala:568)
at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:584)
at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:587)
So this my "unwiring" which seems to be working as expected:
import scala.tools.nsc.interpreter._
import scala.tools.jline.console.completer.{Completer, ArgumentCompleter}
import scala.tools.nsc.interpreter.Completion.{Candidates, ScalaCompleter}
import scala.tools.nsc.Settings
import collection.JavaConverters._
object Completion2 extends App {
val settings = new Settings
settings.usejavacp.tryToSetFromPropertyValue("true")
val intp = new IMain(settings)
intp.initializeSynchronous()
val completion = new JLineCompletion(intp)
def scalaToJline(tc: ScalaCompleter): Completer = new Completer {
def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = {
val buf = if (_buf == null) "" else _buf
val Candidates(newCursor, newCandidates) = tc.complete(buf, cursor)
newCandidates foreach (candidates add _)
newCursor
}
}
val argCompletor: ArgumentCompleter =
new ArgumentCompleter(new JLineDelimiter, scalaToJline(completion.completer()))
argCompletor.setStrict(false)
val jlist: java.util.List[CharSequence] = new java.util.ArrayList
val buffer = "val x = Indexe"
argCompletor.complete(buffer, buffer.length, jlist)
val list = jlist.asScala
println("----BEGIN COMPLETION----")
list.foreach(println)
println("----END COMPLETION----")
intp.close()
}
Edit: This has problems with wildcard imports for some reason. Like if I execute
import mypackage.MySymbol
Then MySymbol is found by the completer. But if I execute instead
import mypackage._
Then none of the contents of mypackage are found. Any ideas?
-Hi. I'd like to embed Scala REPL with initialized environment into my app. I've looked at IMain class and it seems I could do it via instance of it. The instance is created and then stored into intp public var in process() of ILoop.
How can I bind some names and/or add some imports before process() (e.g. before REPL)?
Following code fails on line 3 because intp is not yet created (=> NPE):
val x = 3
val interp = new ILoop
interp.bind("x", x) // -> interp.intp.bind("x", x)
val settings = new Settings
settings.usejavacp.value = true
interp.process(settings)
Thank you-.
UPDATE: Overriding createInterpreter() unfortunately doesn't work:
val x = 3
val interp = new ILoop {
override def createInterpreter() {
super.createInterpreter()
intp.bind("x", x) // -> interp.intp.bind("x", x)
}
}
val settings = new Settings
settings.usejavacp.value = true
interp.process(settings)
Interpreter is stuck on input (looks like deadlock, happens only with code above):
x: Int = 3
Failed to created JLineReader: java.lang.NoClassDefFoundError: scala/tools/jline/console/completer/Completer
Falling back to SimpleReader.
Welcome to Scala version 2.9.2 (OpenJDK 64-Bit Server VM, Java 1.7.0_06-icedtea).
Type in expressions to have them evaluated.
Type :help for more information.
scala> println
<infinite_sleep>
Thanks dvigal for suggestion.
There is a github project called scala-ssh-shell which may do what you want, or at least get you closer.
-Hi, sorry I not Scala REPL hacker but i think you can do something like:
class YourILoop(in0: Option[BufferedReader], protected override val out: JPrintWriter)
extends ILoop(in0, out) {
override def createInterpreter() {
if (addedClasspath != "")
settings.classpath append addedClasspath
intp = new ILoopInterpreter
val x = 3;
intp.bind("x", x)
}
}
object Run {
def errorFn(str: String): Boolean = {
Console.err println str
false
}
def process(args: Array[String]): Boolean = {
val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x))
import command.{ settings, howToRun, thingToRun }
new YourILoop process settings
}
def main(args: Array[String]) {
process(args)
}
}