Debugging Scala sequences in IDE? - scala

When I try to debug the following code in either IntelliJ or Scala IDE, the debugger gets stuck on the breakpoint of the first line and tries to step through all 100 iterations. IntelliJ's run to cursor doesn't skip over the line and eclipse doesn't either. Any ideas on how to handle this case?
object Test extends App {
val data: Array[Int] = (0 to 100).map(i => (i+1)).toArray
println(data)
}

In Eclipse, it looks like the breakpoint on the line with the map is at the apply of the anonfun, and trying to step in brings you back to the breakpoint, while stepping over leaves you at the specialized apply.
But you can also select a stack frame and set a breakpoint there, for instance at the result at the end of TraversableLike.map. You can disable the first breakpoint (in the breakpoints view) to get there quickly.
Then step-return and step-in gets you to the newArray.
You can use javap in the usual way to see what you stepped in:
javap -p -v bin/Test\$\$anonfun\$1.class

Related

Adding debug points in for-comprehension / partial functions

How can I add debug points to statements in for-comprehension?
for {
a <- sqlQuery1()
b <- sqlQuery2()
} yield {
// output
}
I want to inspect as each of the above queries are executed, but simply adding debug points does not seem to work. The queries get executed without ever hitting debug points. I am not sure if it is a limitation of the library being used, language or IDE.
Here is my setup:
Scala
IntelliJ IDEA 14.1.3
Scala Plugin 1.5.200
Library - Slick 3.0 (Using DBIO Actions)
Update:
Case 1: with slick
Debug points don't work. Confirmed that again.
Case 2: w/o slick
Debugger does stop at debug points. But sometimes it evaluates expressions and sometimes it does not. I have noticed following problems (in currying / anonymous functions):
Variable value is shown as 'Size = ?'
Variable value is 'Debug info unavailable'
Fails to evaluate expression
Cursor stays on the same line without highlighting anything whenever I step over (it is doing things in the background though)
'Warning: No executable code found at...'
Actually searching without slick as keyword showed me that a lot of people have similar issues, like here.

toString on a negative number doesn't compile in Scala Worksheet

If I create a Scala Worksheet in Eclipse as follows:
object negative {
2.toString //> res0: String = 2
(2).toString //> res1: String = 2
// compile error
(-2).toString
}
the final line causes a compile error:
';' expected but ')' found. illegal start of simple expression
However, the same three lines compile and run fine within a normal Scala source file.
Why does this not work in the worksheet?
This is using Eclipse 3.7.2, Scala IDE 3.0.0.v-2_10, Scala Worksheet 0.1.4.v-2_10
[Updated: this question originally used toBinaryString, but the problem occurs even with toString, so I have simplified it]
It is a bug. The code in the main object (the first one) of a worksheet is instrumented before being executed. In the 2 mentioned case, the result of the instrumentation is not valid Scala code.
But it is only a problem if the code is at the top level in the main object. If the code is moved to a function or a different object in the same file, it works fine.
Eclipse worksheets are quite beta; for example last I checked, it couldn't handle a #tailrec decoration on a function.
So this is most probably a bug or limitation in Eclipse. After all, the feature seems quite new, and there are many other bugs.
(-2).toBinaryString
gives same error for me.
Note that java.lang.Integer.toBinaryString(-2)works just fine.

Alternative to Scala REPL breakIf in 2.10

I was reading here about using the breakIf method in the REPL code for interactive debugging, but then I found this post saying that break and breakIf were removed from ILoop in Scala 2.10. Unfortunately, that post doesn't explain why the code was removed.
I'm assuming that these functions were removed because there's a better way of doing this. If that's the case, could someone please enlighten me?
Perhaps the idea is that you should just work with the ILoop directly? As far as I can tell, it shouldn't be much more complex than:
// insert the code below wherever you want a REPL
val repl = new ILoop
repl.settings = new Settings
repl.in = SimpleReader()
repl.createInterpreter()
// bind any local variables that you want to have access to
repl.intp.bind("i", "Int", i)
repl.intp.bind("e", "Exception", e)
// start the interpreter and then close it after you :quit
repl.loop()
repl.closeInterpreter()
Compared to the old breakIf API, this approach gets rid of an additional level of indirection for both the if condition (which was wrapped into a => Boolean) and the DebugParam/NamedParam (which were temporary wrappers used only to fill in the bind arguments).
This approach also allows you to specify your Settings as needed. For example, some REPL bugs can be worked around with -Yrepl-sync but break gave you no way of specifying that.

Internal scala compilation. Working with interactive.Global

I am trying to retrieve the AST from scala souce file. I have simplified the code (only relevant code) to following.
trait GetAST {
val settings = new Settings
val global = new Global(settings, new ConsoleReporter(settings))
def getSt = "hello" //global.typedTree(src, true)
}
object Tre extends GetAST {
def main(args:Array[String])
{
println(getSt.getClass)
println("exiting program")
}
}
The above code compiles fine and runs fine. But the problem is the program does not exit. The prompt is not displayed after printing "exiting program". I have to use ^c to exit.
Any idea what the problem might be
I believe Michael is correct, the compiler uses Threads and therefore the JVM doesn't just exit.
The good news is that interactive.Global mixes in interactive.CompilerControl trait whose askShutdown method you can call at the end of your main to let the program exit.
Without knowing what Settings, Global and ConsoleReporter are nobody can give you an exact answer. I would guess that at least one of them is creating a thread. The JVM waits until all threads are done (or all running are deamon threads). See here.
I would bet if you comment out the settings and global lines it will exit as expected.

How to reload a class or package in Scala REPL?

I almost always have a Scala REPL session or two open, which makes it very easy to give Java or Scala classes a quick test. But if I change a class and recompile it, the REPL continues with the old one loaded. Is there a way to get it to reload the class, rather than having to restart the REPL?
Just to give a concrete example, suppose we have the file Test.scala:
object Test { def hello = "Hello World" }
We compile it and start the REPL:
~/pkg/scala-2.8.0.Beta1-prerelease$ bin/scala
Welcome to Scala version 2.8.0.Beta1-prerelease
(Java HotSpot(TM) Server VM, Java 1.6.0_16).
Type in expressions to have them evaluated.
Type :help for more information.
scala> Test.hello
res0: java.lang.String = Hello World
Then we change the source file to
object Test {
def hello = "Hello World"
def goodbye = "Goodbye, Cruel World"
}
but we can't use it:
scala> Test.goodbye
<console>:5: error: value goodbye is not a member of object Test
Test.goodbye
^
scala> import Test;
<console>:1: error: '.' expected but ';' found.
import Test;
There is an alternative to reloading the class if the goal is to not have to repeat previous commands. The REPL has the command
:replay
which restarts the REPL environment and plays back all previous valid commands. (The invalid ones are skipped, so if it was wrong before, it won't suddenly work.) When the REPL is reset, it does reload classes, so new commands can use the contents of recompiled classes (in fact, the old commands will also use those recompiled classes).
This is not a general solution, but is a useful shortcut to extend an individual session with re-computable state.
Note: this applies to the bare Scala REPL. If you run it from SBT or some other environment, it may or may not work depending on how SBT or the other environment packages up classes--if you don't update what is on the actual classpath being used, of course it won't work!
Class reloading is not an easy problem. In fact, it's something that the JVM makes very difficult. You do have a couple options though:
Start the Scala REPL in debug mode. The JVM debugger has some built-in reloading which works on the method level. It won't help you with the case you gave, but it would handle something simple like changing a method implementation.
Use JRebel (http://www.zeroturnaround.com/jrebel). JRebel is basically a super-charged class reloading solution for the
JVM. It can handle
member addition/removal, new/removed classes, definition changes, etc. Just about the only thing it can't handle is changes in class hierarchy (adding a super-interface, for
example). It's not a free tool, but they do offer a complementary license which is limited to Scala compilation units.
Unfortunately, both of these are limited by the Scala REPL's implementation details. I use JRebel, and it usually does the trick, but there are still cases where the REPL will not reflect the reloaded class(es). Still, it's better than nothing.
There is an command meet you requirement
:load path/to/file.scala
which will reload the scala source file and recompiled to classes , then you can replay you code
This works for me....
If your new source file Test.scala looks something like this...
package com.tests
object Test {
def hello = "Hello World"
def goodbye = "Goodbye, Cruel World"
}
You first have to load the new changes into Scala console (REPL).
:load src/main/scala/com/tests/examples/Test.scala
Then re-import the package so you can reference the new code in Scala console.
import com.tests.Test
Now enjoy your new code without restarting your session :)
scala> Test.goodbye
res0: String = Goodbye, Cruel World
If the .scala file is in the directory where you start the REPL you can ommit the full path, just put :load myfile.scala, and then import.