scala command skips running main if class outside of singleton object - scala

EDIT: Main method is not called in Scala script is related (in particular, the answer from Régis Jean-Gilles). This post gives more details to describe the issue. And the answer (by suish) give a more practical demonstration to explain the behaviour of the scala command.
Content of MiniScalaApp.scala
object MiniScalaApp {
def main(args: Array[String]) = {
println(s"Scala Version: ${scala.util.Properties.scalaPropOrElse("version.number", "unknown")}")
println(new Dinosaur("Tyrannotitan", 4900))
println(new Dinosaur("Animantarx ", 300))
}
class Dinosaur (name:String, weightKG: Int) {
override def toString = f"$name, Weight: $weightKG kg"
}
}
Executed at the command line by:
$ scala /myProject/src/main/scala/MiniScalaApp.scala
Produces the expected output:
Scala Version: 2.11.7
Tyrannotitan, Weight: 4900 kg
Animantarx, Weight: 300 kg
However, if the Dinosaur class is placed outside of the singleton object MiniScalaApp then the scala command produces no console output, no error message.
object MiniScalaApp {
def main(args: Array[String]) = {
println(s"Scala Version: ${scala.util.Properties.scalaPropOrElse("version.number", "unknown")}")
println(new Dinosaur("Tyrannotitan", 4900))
println(new Dinosaur("Animantarx ", 300))
}
}
class Dinosaur (name:String, weightKG: Int) {
override def toString = f"$name, Weight: $weightKG kg"
}
In this 2nd version, to get the console output, the code must be compiled first and then run the MiniScalaApp.class separately
$ scalac /myProject/src/main/scala/MiniScalaApp.scala
$ scala MiniScalaApp
Question: What is the reason the scala command treats the code differently?

scala -help explains all.
A file argument will be run as a scala script unless it contains only
self-contained compilation units (classes and objects) and exactly one
runnable main method. In that case the file will be compiled and the
main method invoked. This provides a bridge between scripts and
standard scala source.
so the latter case which is defining object and class, It will run the code as script.
in another way to say, what It does is exactly the same as...
scala> :paste
// Entering paste mode (ctrl-D to finish)
object MiniScalaApp {
def main(args: Array[String]) = {
println(s"Scala Version: ${scala.util.Properties.scalaPropOrElse("version.number", "unknown")}")
println(new Dinosaur("Tyrannotitan", 4900))
println(new Dinosaur("Animantarx ", 300))
}
}
class Dinosaur (name:String, weightKG: Int) {
override def toString = f"$name, Weight: $weightKG kg"
}
// Exiting paste mode, now interpreting.
defined object MiniScalaApp
defined class Dinosaur
only defining.so you need to call it explicitly.
MiniScalaApp.main(Array())
In addition to that, object Foo extends App can't be used if the file have only one top-lebel object.def mainis required.

Seems like you need to invoke the main method explicitly if there is more than 1 top-level class/object:
object MiniScalaApp {
def main(args: Array[String]) = {
println(s"Scala Version: ${scala.util.Properties.scalaPropOrElse("version.number", "unknown")}")
println(new Dinosaur("Tyrannotitan", 4900))
println(new Dinosaur("Animantarx ", 300))
}
}
class Dinosaur (name:String, weightKG: Int) {
override def toString = f"$name, Weight: $weightKG kg"
}
MiniScalaApp.main(args);
See here: Main method is not called in Scala script

Related

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.

Scala macro annotation typecheck for implicit class fails

Macro sample code:
package macros
import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
class Ant extends StaticAnnotation {
def macroTransform(annottees: Any*): Unit = macro Ant.impl
}
object Ant {
def impl(c: Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
c.internal.enclosingOwner.asType.toType // this line is Ok
// ! Any commented line below causes the same compilation error
// c.internal.enclosingOwner.asType.toType.decls
// c.mirror.staticClass(c.internal.enclosingOwner.fullName + ".A".toString)
// c.typecheck(annottees.head)
q"""implicit class A(val v: Int) extends AnyVal { def ask() = println("ok") }"""
}
}
Changing whitebox.Context to macros.Context or blackbox.Context does not help.
Changing arguments withImplicitViewsDisabled=true, or withMacrosDisabled=true has no effect.
Exec sample code:
package test
import macros.Ant
object Test extends App {
val a = new A(42)
a.ask() // Output should be "ok" (not 42)
// ! removing [implicit] lets code be compiled
#Ant implicit class A(v: Int) { def ask() = println(v)}
}
So, removing line c.typecheck(annottees.head) and / or word implicit in line #Ant implicit class A(v: Int) lets code be compiled.
Otherwise compilation crashes with error:
Error:scalac:
no progress in completing object Test: <?>
while compiling: D:\Projects\_Schemee\TestMacro1\src\test\Test.scala
during phase: globalPhase=typer, enteringPhase=namer
library version: version 2.11.6
compiler version: version 2.11.6
reconstructed args: -nobootcp -classpath ...
last tree to typer: Ident(v)
tree position: <unknown>
tree tpe: Int
symbol: value v
symbol definition: v: Int (a TermSymbol)
symbol package: test
symbol owners: value v -> method A -> object Test
call site: method A in object Test in package test
<Cannot read source file>
Compiled under latest IntelliJ. With and without Sbt.
The question is: how to use typecheck in macro annotation with implicit classes? (or am i missing something?)
EDITED:
Besides that that error is caused when trying to access enclosingOwner declarations or mirror class A "manually".
Github link
Issue link
It looks like an sbt bug or interaction with compiler behavior.
The original exception:
java.lang.NullPointerException
at xsbt.Dependency$ExtractDependenciesByMemberRefTraverser$$anonfun$1.isDefinedAt(Dependency.scala:142)
That location:
val typeSymbolCollector = new CollectTypeTraverser({
case tpe if !tpe.typeSymbol.isPackage => tpe.typeSymbol
})
The traverser has a comment suggesting similar issues:
/*
* Some macros appear to contain themselves as original tree.
* We must check that we don't inspect the same tree over and over.
* See https://issues.scala-lang.org/browse/SI-8486
* https://github.com/sbt/sbt/issues/1237
* https://github.com/sbt/sbt/issues/1544
*/

Using an object inside a class in same scala file

The following is the example that I am working on,
package com.sandbox.scala
class ScalaOne {
def main(args: Array[String]){
com.sandbox.scala.sayHello.hello(); //This works
sayHello.hello(); //error: not found: value sayHello
}
}
object sayHello{
def hello(){
println("Hello from Scala");
}
}
As you may see the second call sayHello.hello() throws an error mentioned in the comment. Why is that? I assumed it should work.

Scala's delayedInit and Scallop

SLS 5.1 says "Delayed Initializaton. The initialization code of an object or class (but not a trait) that follows the superclass constructor invocation and the mixin-evaluation of the template’s base classes is passed to a special hook, which is inaccessible from user code. Normally, that hook simply executes the code that is passed to it. But templates inheriting the scala.DelayedInit trait can override the hook by re-implementing the delayedInit method, which is defined as follows:"
def delayedInit(body: => Unit)
The ScallopConf command-line parser extends DelayedInit and using it according to the docs generates the warning Selecting value apples from class Conf, which extends scala.DelayedInit, is likely to yield an uninitialized value.
How should the following simple example be rewritten so the warning is not generated?
import org.rogach.scallop._
class Conf(arguments: Seq[String]) extends ScallopConf(arguments) {
val help = opt[Boolean](name = "help", short = 'h', descr = "Help me please.")
}
object Gen {
def main(args: Array[String]) {
val conf: Conf = new Conf(args)
if (conf.help()) {
println(s"""Usage: Gen [--help]""")
sys.exit(-1)
}
println("Do some work here")
}
}
Making help a lazy val and calling conf.afterInit() before accessing conf.help should clear the compiler warnings.

Categories