Hi I'm newbie in Scala.
As far as I know there are 2ways to make entry point in scala, one is define main method with object and the other is extending App trait.
I wondered how App trait works, so I checked the source for App trait, but there are full of confusing code...
The code said that the App has initCodes which are extended from App trait, and these are added in delayedInit method that inherited from DelayedInit. Also the App trait has main method, which will be entry point.
But the What confusing me are
Who call delayedInit? Is it called before the main method is called?(I guess Yes)
Why initCodes is ListBuffer not a element? I think there is only one entry point in application, so I don't think it should be plural.
Where can I check these knowledge? I tried to search in document but I couldn't
Who call delayedInit? Is it called before the main method is called?(I guess Yes)
The delayedInit would be called automatically by the Scala compiler as the initialisation code of the object/class that extends the DelayedInit trait. I expand more on this answer below.
Why initCodes is ListBuffer not a element? I think there is only one entry point in application, so I don't think it should be plural.
Because it is possible to have a hierarchy of classes, where the initialisation code of each class in the hierarchy gets executed as part of executing the program. An example is also provided below.
Where can I check these knowledge? I tried to search in document but I couldn't.
I got to learn about the dynamics by reading the Scala docs and the links it points to. For example this https://github.com/scala/scala/releases/tag/v2.11.0 and https://issues.scala-lang.org/browse/SI-4330?jql=labels%20%3D%20delayedinit%20AND%20resolution%20%3D%20unresolved
I would now try to expatiate more on the answer above by going into more details into the workings of DelayedInit, and how the JVM specifies entry points to programs.
First of all, we have to understand that when Scala is run on the JVM, it still has to adhere to the JVM requirement for defining the entry point to your program, which is to provide the JVM with a class with a main method with signature of public static void main(String[]). Even though when we use the App trait, it might appear as if we are getting away from do this, but this is just an illusion, the JVM still needs to have access to a method with the signature public static void main(String[]). It is just that by extending App together with the mechanism of DelayedInit, Scala can provide this method on our behalf.
Second, it is also good to reiterate that code snippets found in the body of a class (or object) definition, would be the initialisation code of such a class/object and would be executed automatically when such is instantiated. In Java, it is more or less the code you put in the constructor block.
So for a class:
class Foo {
// code.
def method = ???
}
Whatever code is, it will be executed automatically when you call new Foo.
In case of an object
object Foo {
// code.
def method = ???
}
The code will be executed automatically without you having to call new since Scala would automatically make a singleton instance called Foo available for you.
So basically if anything is in the body definition, it gets executed automatically. You do not need to explicitly execute it.
Now to the DelayedInit trait. One thing to be aware of is that it provides us a mechanism to perform what can be called a compiler trick, where certain part of our code gets rewritten. This is one of the reason why it could be confusing to reason about. Because when you use it, what actually gets executed by the Scala compiler is not the code you reading but a slight modification of it. To understand what is going on, you then need to understand the ways the compiler alters the code.
The trick, the DelayedInit trait allows us to perform is to take the code that is part of the body of a class/object definition and turn it, into an argument that is passed by name, to the method delayedInit defined on DelayedInit.
Basically it rewrites this:
object Foo {
// some code
}
into
object Foo {
// delayedInt({some code})
}
This means instead of having // some code executed automatically, delayedInt is the method that is called automatically with // some code passed to it as arguments.
So anything that extends DelayedInit would have its initialisation code replaced by the method call delayedInt with the initialisation code passed as an argument. Hence why nobody needs to explicitly call the delayedInt method.
Now let use see how this then tie to the App trait and how the App trait is used to provide the entry point to a Scala application.
As you will notice, the delayedInit method on the DelayedInit trait does not provide any implementation. Hence the actual behaviour of delayedInit when it is called needs to be provided by something else that extends DelayedInit.
The App trait is such an implementation. And what does the App trait do? Two important thing related to the topic of discussion:
It provides an implementation of delayedInit which takes the initialisation code it is passed, and puts it in a ListBuffer.
It provides the main method def main(args: Array[String]) which satisfy the requirement of the JVM to have a method with public static void main(String[]) to serve as the entry point to a program. And what this main method does, is to execute whatever code placed in the ListBuffer.
The above characteristics of the App trait means that any object/class that extends it would have its initialisation code passed to delayedInit, which would then add it into a ListBuffer, and then the object/class extending it would now have a main method, which when called (most of the time by the JVM as the entry point) would run through the code in the ListBuffer and execute it.
Basically it turns this:
object Foo {
// some code
}
into this
object Foo {
// the implementation of delayedInt is to put `// some code` into a list buffer
delayedInt (// some code)
def main(args: Array[String]) = {
// the implementation below just runs through and execute the code found in list buffer that would have been populated by the call to delayedInt and
???
}
}
So why have a List buffer to store the code to be executed? Because, as I said above it is possible to have a hierarchy of classes, where the initialisation code of each class in the hierarchy gets executed as part of executing the program. To see this in action.
Given the following code snippet:
class AnotherClass {
println("Initialising AnotherClass")
}
trait AnotherTrait {
println("Initialising AnotherTrait")
}
trait YetAnotherTrait {
println("Initialising YetAnotherTrait")
}
object Runner extends AnotherClass with AnotherTrait with YetAnotherTrait with App {
println("Hello world")
}
When run would output the following:
Initialising AnotherClass
Initialising AnotherTrait
Initialising YetAnotherTrait
Hello world
So the individual initialisation code in the hierarchy that consists of AnotherClass, AnotherTrait and YetAnotherTrait gets added to the initCode list buffer, via the delayedInit method of the App trait, and then they get executed by the main method also provided by the App trait.
As you would have noticed by peeking into the source code, the whole mechanism of DelayedInt is deprecated and schedule for removal in the future.
delayedInit:-
The init hook. This saves all initialization code for execution within
main. This method is normally never called directly from user code.
Instead it is called as compiler-generated code for those classes and
objects (but not traits) that inherit from the DelayedInit trait and
that do not themselves define a delayedInit method.
App scala
delayedInit is deprecated since 2.11.0. Also, it has some outstanding bugs which are not fixed.
initCodes:-
I am not sure about the reason for defining the initCodes as ListBuffer.
Related
What is the difference between
object Application extends App {
println("Hello World")
}
and
object Application {
def main(args: Array[String]): Unit = {
println("Hello World");
}
}
The App trait is a convenient way of creating an executable scala program. The difference to the main method altenative is (apart from the obvious syntactic differences) that the App trait uses the delayed initalization feature.
From the release notes for 2.9 (see http://www.scala-lang.org/old/node/9483 )
Objects inheriting the App trait instead make use of Scala 2.9’s delayed initialization feature to execute the whole body as part of an inherited main method.
Another new feature of the App scheme is that command line arguments are now accessible via the args value (which is inherited from trait App)
These two cases is not same on the scala scripting.
object extends App was not executed by "scala MyObject.scala" command,
but the object containing the main method was executed by "scala MyObject.scala" command.
Which was described as scala looking for object with main method for scripting.
When using REPL or scala workseet of Eclipse,
need to call MyObject.main(Array[String]()) explicitly for both cases.
This simple tip be helpful for beginner like me.
App trait is implemented using the [[DelayedInit]] functionality, which means that fields of the object will not have been initialized before the main method has been executed.
Now i understand how my program runs after extending App trait .I went through this link to understand how App trait works . In the link it is mentioned that by extending App trait we are achieving lazy evaluation . Why i would need lazy evaluation ? How lazy evaluation better than direct call to main() instead of extending App trait ?
I think it's the other way around: we don't need lazy eval, but we use it behind the scenes because that's the only way to implement it. From the scaladoc:
The App trait can be used to quickly turn objects into executable
programs.
By using App trait you avoid the boilerplate of writing:
object MainApp {
def main(args: Array[String]): Unit = { ... }
}
There is no way to achieve this syntax: object MainApp extends App {...} with regular means because you would have to override main method to call your code. Thus you can use compiler trick with DelayedInit which will turn your object body into a function call which will be called from main - this is the way to connect your code to the main entry point.
The caveat mentioned by scaladoc is:
It should be noted that this trait is implemented using the
[[DelayedInit]] functionality, which means that fields of the object
will not have been initialized before the main method has been
executed.
which for me personally is a preferred way of doing things. This, in contrast, is different from static initializers in Java that are executed before main method is called.
I've just started learning Scala and many of the tutorials that I'm following are using a combination of different representations for a main method. Aside from the familiar main method; there's also the use of traits App or Application.
It appears that Application has been deprecated and is not recommended, but I can't find any information that explains much beyond this about each of these ways to define an entry point.
So, I'm wondering if someone could explain to me:
How do the traits App and Application work?
Why is the trait Application no longer recommended and what does the App trait do that is different?
Where should I use the traditional main method and when should I use App to start my program? What's the difference between the two approaches?
The problem with the Application trait is actually described in its documentation:
(1) Threaded code that references the object will block until static initialization is complete. However, because the entire execution of an object extending Application takes place during static initialization, concurrent code will always deadlock if it must synchronize with the enclosing object.
This is a tricky one. If you extend the Application trait, you are basically creating a Java class:
class MyApplication implements Application {
static {
// All code goes in here
}
}
The JVM runs the above class initializer implicitly synchronized on the MyApplication class. This way, it is assured that no instance of MyApplication is created before its class is initialized. If you spawn a thread from your application that again needs to access an instance of MyApplication, your application would dead lock as the class initialization is only complete after the entire program has executed. This implies a paradox as no instance can be created as long as your program is running.
(2) As described above, there is no way to obtain the command-line arguments because all code in body of an object extending Application is run as part of the static initialization which occurs before Application's main method even begins execution.
A class initializer does not take any arguments. Also, it is run first, before any values could be handed to the class as the class initializer needs to be executed before you could even assign a static field value. Thus, the args that you normally receive on a main method are lost.
(3) Static initializers are run only once during program execution, and JVM authors usually assume their execution to be relatively short. Therefore, certain JVM configurations may become confused, or simply fail to optimize or JIT the code in the body of an object extending Application. This can lead to a significant performance degradation.
The JVM optimizes code that is run frequently. This way, it makes sure that no run time is wasted on methods that are not really a performance bottle neck. However, it safely assumes that static methods are only executed once as they cannot be invoked manually. Thus, it will not optimize the code that is run from a class initializer which is however your application's main method code if you are using the Application trait.
The App trait fixes all this by extending DelayedInit. This trait is explicitly known to the Scala compiler such that initialization code is not run from a class initializer but from another method. Note the for name reference that is haded to the trait's only method:
trait Helper extends DelayedInit {
def delayedInit(body: => Unit) = {
println("dummy text, printed before initialization of C")
body
}
}
When implementing DelayedInit, the Scala compiler wraps any initialization code of its implementing class or object into a for name function which is then passed to the delayedInit method. No initialization code is executed directly. This way, you can also run code before an initializer is run what allows Scala for example to print an application's runtime metrics to the console which is wrapped around the program's entry point and exit. However, there are some caveats of this approach and using DelayedInit is therefore deprecated. You should really only rely on the App trait which solves the problems that are imposed by the Application trait. You should not implement DelayedInit directly.
You can still define a main method if you want to, as long as you define it in an object. This is mostly a matter of style:
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}
What is the difference between
object Application extends App {
println("Hello World")
}
and
object Application {
def main(args: Array[String]): Unit = {
println("Hello World");
}
}
The App trait is a convenient way of creating an executable scala program. The difference to the main method altenative is (apart from the obvious syntactic differences) that the App trait uses the delayed initalization feature.
From the release notes for 2.9 (see http://www.scala-lang.org/old/node/9483 )
Objects inheriting the App trait instead make use of Scala 2.9’s delayed initialization feature to execute the whole body as part of an inherited main method.
Another new feature of the App scheme is that command line arguments are now accessible via the args value (which is inherited from trait App)
These two cases is not same on the scala scripting.
object extends App was not executed by "scala MyObject.scala" command,
but the object containing the main method was executed by "scala MyObject.scala" command.
Which was described as scala looking for object with main method for scripting.
When using REPL or scala workseet of Eclipse,
need to call MyObject.main(Array[String]()) explicitly for both cases.
This simple tip be helpful for beginner like me.
App trait is implemented using the [[DelayedInit]] functionality, which means that fields of the object will not have been initialized before the main method has been executed.
In Eclipse plugin, I could create scala Object, APP and class. But what's the difference among these 3. Basically, for object, I implement def main(...), then I could run it. But in class, it seems it is similar to normal Java class, but what's the meaning scala APP, it creates a object which extends APP. What's this meant for?
In the JVM, the starting point of the application is a static method main(String[] args), in some class given to the JVM.
In scala there is no static methods, the equivalent is to put a method in an object (as opposed to class). Putting your main method in a class will not work, it is the same as not marking the method static in java.
App is a helper that allows not to write the main method, and put the code directly in the object body.
object MyApp extends App {
doStuff
}
does the same thing as
object MyApp {
def main(args: Array[String]) {doStuff}
}
(arguments can be used in doStuff under the name args)
Scala has no static methods, so public static void main is not available for classes.
Objects are single entries that better fits the "common to every object of a class" scope. So, oversimplifying things, you can think of Object as a Singleton which "acts as an only-static container" (quote from Scala for Java Refugees). If you think about it, that's the perfect place for a main method, and that's why they are declared there.
The App trait just explores the fact that instructions written inside the body of an Object are executed as part of its initialization. It couples this standard behaviour with some delayed initialization tricks to use the body of an object as the main method. So, you can think about it as a short alternative syntax to create a application entry point. It replaced the naive Application trait that had a lot of shortcomings.
Which one of the two to use is really a matter of taste, but I tend to stick with the more familiar main method syntax.
Cheers,