Reputation: 199
I am running the Scala REPL, version 2.12.4, on windows 10. When I press CTRL-C, I am prompted: "Terminate batch job (Y/N)?". However, no matter what I type, the REPL always stops and I have to restart it.
How can I avoid exiting the REPL?
Upvotes: 1
Views: 142
Reputation: 59
If you use REPL a lot, you may want to consider Ammonite. It handles Ctrl-C gracefully out of the box.
It also provides some other nice features like syntax highlighting & scripting for REPL-heavy applications.
Upvotes: 0
Reputation: 36229
If you know upfront, that you run into such issues, you can prepare for it.
Here the steps which didn't worked, until I found a solution:
object InterruptTest {
// this method takes some seconds, longer and longer, a few seconds
// until it reaches 45 on my some years old machine, enough time
// to interrupt it. For larger values, you soon have to increase
// startparams, like:
// scala -J-Xms512m -J-Xmx4G InterruptTest 80
@annotation.tailrec
def partition (count: Int, ziel: Int, sofar: Vector [Vector [Int]]) : Vector [Vector [Int]] = {
println (s"i: $count\tlen: ${sofar.size}")
if (count == ziel) sofar else {
val ll = Vector (sofar.view.map (li => (li.head + 1) +: li.tail), sofar.view.map (li => 1 +: li)).flatten.map(_.sorted).distinct
partition (count+1, ziel, ll)
}
}
def main (args: Array[String]) : Unit = try {
args.map (s => partition (1, s.toInt, Vector (Vector (1))))
} catch {
case ie: InterruptedException => println ("ie: exit gracefully")
case re: RuntimeException => println ("re: exit gracefully")
}
}
val test = InterruptTest
test.main (Array ("45"))
Well - it doesn't work. Ctrl-C is still immediate exit. Worth a try, if you happen to not knowing the specification details.
object InterruptTest {
val sc = new java.util.Scanner (System.in)
@annotation.tailrec
def partition (count: Int, ziel: Int, sofar: Vector [Vector [Int]]) : Vector [Vector [Int]] = {
println (s"i: $count\tlen: ${sofar.size}")
// if there is a token, test whether 'q', then quit
if (sc.hasNext) {
val token = sc.next()
if (token.trim () == "q") {
println ("User request: quit, incomplete result!")
return sofar
}
else println ("unknown user request: " + token + " will proceed, use 'q' to quit.")
}
if (count == ziel) sofar else {
val ll = Vector (sofar.view.map (li => (li.head + 1) +: li.tail), sofar.view.map (li => 1 +: li)).flatten.map(_.sorted).distinct
partition (count+1, ziel, ll)
}
}
// time scala -J-Xms512m -J-Xmx4G Partition 80
def main (args: Array[String]) : Unit =
args.map (s => partition (1, s.toInt, Vector (Vector (1))))
}
val test = InterruptTest
test.main (Array ("45"))
A brilliant idea, except: Scanner.hasNext is blocking. So we need a separate Thread. I'm sure there are more elegant solutions than this, with Akka, with Futures and so on. Take it as proof of concept:
object InterruptTest {
// still around: The Scanner-guy:
val sc = new java.util.Scanner (System.in)
// ... but now with volatile:
@volatile
var interrupted = false
// ... and Meanwhiler:
object Meanwhiler extends Thread {
override def run () : Unit = {
if (sc.hasNext) {
val token = sc.next ()
if (token.trim () == "q") {
println ("User request: quit, incomplete result!")
interrupted = true
}
else println ("unknown user request: " + token + " will proceed, use 'q' to quit.")
}
}
}
@annotation.tailrec
def partition (count: Int, ziel: Int, sofar: Vector [Vector [Int]]) : Vector [Vector [Int]] = {
println (s"i: $count\tlen: ${sofar.size}")
if (count == ziel || interrupted ) sofar else {
val ll = Vector (sofar.view.map (li => (li.head + 1) +: li.tail), sofar.view.map (li => 1 +: li)).flatten.map(_.sorted).distinct
partition (count+1, ziel, ll)
}
}
// time scala -J-Xms512m -J-Xmx4G Partition 80
def main (args: Array[String]) : Unit = {
val mw = Meanwhiler
mw.start ()
args.map (s => partition (1, s.toInt, Vector (Vector (1))))
mw.join ()
}
}
val test = InterruptTest
test.main (Array ("45"))
Scanner is still blocking, but not blocking us! Session output:
scala InterruptTest.scala
Loading /home/stefan/lib/predef.scala...
import scala.annotation.tailrec
import scala.io._
import scala.util.Random
Loading InterruptTest.scala...
defined object InterruptTest
test: InterruptTest.type = InterruptTest$@65ae095c
i: 1 len: 1
i: 2 len: 2
i: 3 len: 3
i: 4 len: 5
i: 5 len: 7
// shortened
i: 33 len: 10143
i: 34 len: 12310
i: 35 len: 14883
i: 36 len: 17977
User request: quit, incomplete result!
i: 37 len: 21637
Welcome to Scala version 2.11.6 (OpenJDK 64-Bit Server VM, Java 1.8.0_151).
Type in expressions to have them evaluated.
Type :help for more information.
scala>
caveats: You have to know before, that your code might loop endlessly. If you do so, another quick-and-dirty method would be an threshold and a counter, probably easier to integrate and faster to write. Don't mind the dirty, little var, which gets eliminated, when the bugs are eliminated.
The design might be improved, if you prefer the manual control over a counter. For example, Scanner can be moved into the Meanwhile object. A better name should be searched for. Maybe a more scala like Solution, maybe an Actor, I'm open for suggestions.
Upvotes: 2