Compiling Scala scripts. How works a Scala script? - scala

Groovy
Groovy comes with a compiler called groovyc. For each script, groovyc generates a class that extends groovy.lang.Script, which contains a main method so that Java can execute it. The name of the compiled class matches the name of the script being compiled.
For example, with this HelloWorld.groovy script:
println "Hello World"
That becomes something like this code:
class HelloWorld extends Script {
public static void main(String[] args) {
println "Hello World"
}
}
Scala
Scala comes with a compiler called scalac.
For example, with the same HelloWorld.scala script:
println("Hello World")
The code is not valid for scalac, because the compiler expected class or object definition, but works in the Scala REPL Interpreter. How is possible? Is it wrapped in a class before the execution?

The code in a Scala-Script is first placed in a Scala object, then compiled to JVM-Bytecode and at last executed. You can see the generated Scala object by writing scala -Xprint:parser my_file.scala:
package <empty> {
object Main extends scala.ScalaObject {
def <init>() = {
super.<init>();
()
};
def main(argv: Array[String]): scala.Unit = {
val args = argv;
{
final class $anon extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
println("hello world")
};
new $anon()
}
}
}
}

scalac will compile your code to java bytecode. println("Hello World") is not by itself a valid scala program, so scalac will not compile it.
You can do:
object Test extends App {
println("Hello World")
}
or
object Test {
def main(args: Array[String]) {
println("Hello World")
}
}
You can then run the output with scala Test.
Running the scala "interpreter" on a file that only contains the println("Hello World") line will basically wrap it in an object (transforming it into the first form we've seen above), compile it and run it for you under the hood.
Note that (even though the code looks very similar to a Hello World console application in java) the resulting compiled program is not the same thing that would result from the compilation of this java
/* note: this is Java code */
/* this does NOT compile to the same bytecode as the previous scala example*/
public class Test {
public static void main (String args[]) {
System.out.println ("Hello World");
}
}

Once you successfully compile a file with scalac, you can use javap, the java class file disassembler, to view the generated class and method signatures (Edit: thanks to #Luigi for pointing out that it is generated signatures, not code). That is, scalac HelloWorld.scala followed by javap HelloWorld.

Related

Why IntelliJ doesn't run my script if it has main in it

I am observing an interesting behavior. I have an existing project in which I created a folder and created a Scala script in that folder. To run it I did
Write a Scala script, e.g. MyScript.scala
In the menu select: Run -> Edit Configurations... Press the "+" (⌘N also works on the Mac in this dialog) Select "Scala Script" Then select your Script file in this dialog
Interestingly, if the script is the following then I get error Scala script not found
object HelloWorld{
def main(args:Array[String]): Unit ={
println("hello world");
}
}
but if the script is
def greetings(): Unit ={
println("hello")
}
greetings();
then it works!
Why IntelliJ cannot run the 1st version of the script?
You could do the following:
Run it as a script.
You have to use the following code:
class HelloWorld {
def main(args:Array[String]): Unit ={
println("hello world");
}
}
object Foo extends HelloWorld
Foo.main(args)
Hint: I removed the 'build' action from 'Before launch' to show the warnings further down.
Run it as an Application.
You can keep your code. Just select 'Application' when creating the configuration.
object HelloWorld{
def main(args:Array[String]): Unit ={
println("hello world");
}
}
Why?
You have to provide an entry point for the script. So you could use the following code:
object HelloWorld {
def main(args:Array[String]): Unit ={
println("hello world");
}
}
HelloWorld.main(args) //without this line, Script is not found!
But this gives an error (expected class or object definition):
An If you try to extend from App trait, you get 2 warnings:
object HelloWorld extends App {
override def main(args:Array[String]): Unit ={
println("hello world");
}
}
HelloWorld.main(args)
So I guess its best to use one of two Solutions above.

Why args are compulsory for main

If I use like below considering I don't need to take arguments, it doesn't detect for Scala in eclipse.
object HelloWorld {
def main(): Unit = {
println("Hello Scala!!!")
}
}
It works fine with args: Array[String]
object HelloWorld {
def main(args: Array[String]): Unit = {
println("Hello Scala!!!")
}
}
Well it's simply a convention on the JVM. You won't be able to invoke your object as entry point when running your program. For example, in Scala.js you have main() without arguments.
If you don't need the arguments you can mixin the App trait:
object HelloWorld extends App {
println("Hello Scala!!!")
}

Run menu item disappear in IntelliJ

I have scala code in IntelliJ as follows:
HelloWorld.scala
object HelloWorld {
//var matchExample = new MatchExample()
def main(args: Array[String]) = {
printHello()
//matchExample.oddEven(1)
}
def printHello() = {
println("hello")
}
}
MatchExample.scala
class MatchExample {
def oddEven(x: Int): String = {
"Hello"
}
}
If I un-comment those two lines and try to do run by right-clicking in object I don't have Run menu item, but if I comment out those two lines then I do have "Run" menu item.
What I am missing?
The reason is that your main method signature(un-comment matchExample.oddEven(1)) is incompatible with what Scala compiler requires for a runnable program.
Yours is (args: Array[String])String, the runnable program's main method signature is (args: Array[String])Unit. If you run your code in Terminal, compiler will emit a warning.
allen:Desktop allen$ scalac HelloWorld.scala
HelloWorld.scala:1: warning: HelloWorld has a main method with
parameter type Array[String], but HelloWorld will not be a runnable program.
Reason: main method must have exact signature (Array[String])Unit
object HelloWorld {
^
one warning found
In Scala, if you wanna write a runnable program, you'd better use App trait.
The post has a detailed explaination on Scala App val initialization in main method.

Main method invocation in scala nsc

I was trying to go through the code for nsc(new scala compiler). I am a little confused about Main.scala. It is implemented as follows:
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* #author Martin Odersky
*/
package scala.tools
package nsc
import scala.language.postfixOps
/** The main class for NSC, a compiler for the programming
* language Scala.
*/
class MainClass extends Driver with EvalLoop {
def resident(compiler: Global): Unit = loop { line =>
val command = new CompilerCommand(line split "\\s+" toList, new Settings(scalacError))
compiler.reporter.reset()
new compiler.Run() compile command.files
}
override def newCompiler(): Global = Global(settings, reporter)
override def doCompile(compiler: Global) {
if (settings.resident) resident(compiler)
else super.doCompile(compiler)
}
}
object Main extends MainClass { }
My first question is, how is Main being called by the compiler process? When I call something along the lines of:
scalac [ <options> ] <source files>
Somewhere along the lines the newCompiler and doCompile is being called, can somebody help me with tracing through how this is being called and how the compiler is being initialized?
Any pointers will be much appreciated.
Thanks
MainClass extends Driver which has the main method:
def main(args: Array[String]) {
process(args)
sys.exit(if (reporter.hasErrors) 1 else 0)
}
At the same time, object Main extends MainClass which means that there is a Main.class file that contains a public static void main(String[] args) forwarder method that actually invokes aforementioned non-static method on object Main. See for example this question for more details on how object is compiled in Scala.
This means that scala.tools.nsc.Main can be used as a main class when running the scala compiler (this is hardcoded somewhere in the scalac script).
newCompiler and doCompile are called by process.

Scala println not working with App trait

When I use the scala App trait, I can't get println to work.
This simple example prints as expected,
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}
But once I introduce the trait it does not,
object HelloWorld extends App {
println("Hello, world!")
}
I get no errors but nothing prints to the console.
Did you compile it first (running scalac HelloWorld.scala)? See this comment: http://www.scala-lang.org/node/9483#comment-40627
Edited to add more explanation:
The first version actually was compiled. Scala files without an explicit main method are run uncompiled as scripts. That means that for your second version, the commands in the file are run sequentially, as though they had been entered into the interpreter--so, the object HelloWorld is created, but no method is called on it. There's more information about Scala as a scripting language here (scroll to Step 5): http://www.artima.com/scalazine/articles/steps.html
Add a line
object HelloWorld extends App {
/* code */
}
HelloWorld.main(args)
at the end of your file.
The Class defines the method but it need to be called too.
According to
http://www.scala-lang.org/api/current/scala/App.html
you want to do
object Main extends App {
Console.println("Hello World: " + (args mkString ", "))
}