Friday, July 25, 2008

'Application' trait considered harmful

Programmers new to Java are often confused and annoyed by the overly complex structure of a basic Java program. One line in particular, "public static void main(String[] args)" stands out as lengthy and cryptic.


class MyJavaApp {
public static void main(String[] args) {
// ... body ...
}
}


Scala, descended from Java and reusing much of Java's ecosystem, has a similar requirement, though a slightly less onerous one.


object MyScalaApp {
def main(args: Array[String]) {
// ... body ...
}
}


Now, Scala claims to be able to scrap Java boilerplate in many cases, and simple program construction is no exception. A very ingenious Scala programmer devised the Application trait, which allows us to further simplify the declaration of a simple program. By simply mixing in the Application trait, we can turn any object into a program.


object MyScalaApp extends Application {
// ... body ...
}


For pedagogy, this second approach is clearly preferable. When teaching Scala to programmers unfamiliar with Java, it's no longer necessary to go into the details of a wordy, vestigial Java pattern. When teaching programmers familiar with Java, it can serve as yet another example of how Scala can make their lives easier.

Unfortunately, the Application trait is evil.

Given the simplicity of the pattern, one would be justified in assuming that both of the Scala code snippets are functionally equivalent. This is wrong. To understand why, let's look at the entire source code for a possible implementation of the Application trait. (The actual source code is slightly more involved, but not in any way that affects this discussion.)


trait Application {
def main(args: Array[String]) {}
}


How does it work?, you might ask. How can it run my code without referencing it in any way? To understand how the Application trait works, it's important to understand Scala constructors.

In Scala, the body of a class also doubles as its primary constructor. Likewise, the body of a singleton object doubles as its only constructor. The Application trait works by running the entirety of the application in the constructor for MyScalaApp. This is a clever trick for reducing a bit of boilerplate code, but it turns out to be a catastrophically bad idea.

It's a bad idea for two reasons.

First, concurrency is broken. The JVM does not allow other threads to access a class during initialization. You can easily get yourself into deadlock by writing concurrent code in classes that use Application. (See Ticket #746)

Second, performance is broken. HotSpot doesn't bother to optimize class initialization. This can cause performance differences of several orders of magnitude in classes that use Application. (See this blog post and the ensuing mailing list discussion.)

While the Application trait may save you a line of code, it comes with many severe and hidden pitfalls. Hopefully it will be removed from the standard library, or at least deprecated. In the meantime, save yourself the headache and avoid Application in your applications.

6 comments:

Antony Stubbs said...

"it comes with many severe and hidden pitfalls. Hopefully it will be removed from the standard library, or at least deprecated. In the meantime, save yourself the headache and avoid Application in your applications."

Oh please! It serves it's purpose beautifully! Just don't do any thing that isn't simple inside that class! Just simply use it to bootstrap your application once it gets anything more complicated than a few lines...

1) Who cares if it breaks concurrency! Just don't use it as an entry point where multiple threads could enter. It's only meant as a simple static entry point for you program.

2) Who cares about performance for a simple boot strap code???

In what situation do you really see this causing harm? In the only use case you mention - teaching Scala to people - it's perfect!

James Iry said...

@antony,

Does it makes sense to teach new people a solution which has downsides that are so subtle? I mean, most solutions have trade-offs. But when it's something you learn while you're still mastering "hello, world" you probably aren't ready to understand the gritty details of JVM thread safety, class initializers, and hot spot optimization.

The Application trait idea is salvageable, I think. But as it stands right now it needs to come with at least a big fat warning label in the documentation and probably even a deprecation until it can be fixed or dropped.

Antony Stubbs said...

You think "hello, world" programs should be concerned about gritty details of JVM thread safety, class initialisers, and hot spot optimisation.?

David R. MacIver said...

No. And I think the transition from Hello World programs to real applications shouldn't have to be either! What you're proposing is basically saying "Here, you know that thing we showed you how to use while you were learning? It's actually totally unsafe to use for anything non-trivial". Additionally, it doesn't seem to have any pedagogical value over using the script runner.

Often one just wants to put code in the main entry point to one's application. Sure, you can do all sorts of things to avoid that so you can use Application, but at that point you're doing more work than just "wrap it in a main method".

The bugs caused by Application are non-obvious and completely undocumented. I know of at least two people who have wasted substantial amounts of time trying to figure them out. It has such a narrow scope of utility and can cause such unpleasant problems that leaving it in as an aid to beginners (when really we should be telling the beginners to run screaming from it) is a completely ridiculous idea.

Landei said...

Just for the record: Scala 2.9 will fix the behavior of Application.

SeregaKiev said...

Author the best! Thank you!
люстры