We could name it the name of the file (what Ammonite does) or possibly with a mangling/suffix/prefix to avoid naming conflicts with normally-defined classes. We could do either, since there’s no backwards compatibility concern since *.sc entrypoint files do not currently exist.
What about hiding implementation details of the main program block from the rest of the package?
I think we should just ban usage of things in the *.sc entrypoint files from *.scala library files. That would more or less already match the best practices in most scripting languages, and wouldn’t hinder any of the benefits described in either of these threads (getting started with single file, growing to a multi-file project, similarity with other languages, …)
What about the static initialization lock?
If we’re going to limit usage of *.sc files in *.scala files as described above, we could wrap the *.sc body in the main method. No more object initialization problems, and the contents of *.sc would be invisible and un-importable from other files as we would want.
I’m quite skeptical about approaches of the kind “Let’s add some convoluted magic to make it easier for beginners!”
Transparency is simplicity. If you are going to have stack traces and build tool documentation referring to main methods, then the easiest is to make it obvious what that is.
I think this is the most elegant approach. It builds on an existing convention (Scala scripts/worksheets), but makes it standard and allows it to be part of full-blown Scala projects seamlessly (each .sc file will generate its own main class).
An .sc file is a “Scala main file”.
Want to see the entry points to a Scala application? Look for the .sc files. Want to run one in particular? Just type sbt run mypackage.foo where foo.sc is the name of the file.
versus a very simple, syntactical-only rewriting which takes the existing known correct thing to do , which is an object with a def main , and just introduces syntactic sugar for it, with an obvious user-level meaning.
I just realized that with the “wrap its contents in main methods” way of handling *.sc files, that makes the *.sc approach also a very simple syntactical only rewriting that introduces sugar for the only correct thing to do!
The only difference would be your main body would be in a separate file/extension rather than mixed in with your code in a special program declaration, further reduce hello-world boilerplate, and it would follow the existing script-file convention closely. Other than those three benefits, the two proposals would be identical
The syntactic rewriting requires a novel language construct (instead of no constructs) and introduces an irregularity into the language, all to save a relatively insignificant amount of boilerplate. It’s just syntactic sugar, but it’s not very much sugar, so it’s not really worth it IMO.
Anyway, I already hinted at some workarounds. Concretely:
The name of the object is foo.Main or foo.ClassFileName.Main
Everything the user writes goes into a main method. foo.Main has hidden vars plus defs corresponding to the vals. The initializer fills these with zeros. The main method fills them in when it “creates” the vals/vars. You use lazy val mechanics rather than object mechanics upon loading the object to run the main method once and init everything.
Write the main method yourself if you want to hide implementation details.
scala Test.scala will not work if it contains a main object, it will run it as a script.
On the other hand, scala Test will look for a compiled class named Test.
I guess the use case we’re addressing is not simple scripting. Here are some use cases which are not covered:
You have a large (compiled) codebase with a lot of entry points. (TBH I’m not sure when this would happen.) The objective is to save the repeated boilerplate.
You want to write a CLI. Writing the boilerplate even once is not ideal.
Scripting. Running scala myscript.scala is inadequate for a few reasons. One is that it doesn’t have access to dependencies, for example from maven central. Ammonite solves this.
Something in between a normal codebase and a script, where normal codebase would mean something that you publish as a polished binary, and script means a single file or a few files. Instead, you want to have a codebase that you can publish as a standalone program, but you also want to easily test things and experiment using scripts. So your codebase (which might be an SBT build) should be accessible from the script.
What about an annotation macro, similar to the ones used by circe and monocle.
@app
class Main(args: Array[String]) {
// ...
}
It would generate something like
class Main(args: Array[String]) {
// ...
}
object Main {
def main(args: Array[String]) = new Main(args)
}
Wouldn’t that solve the issue of the initializer lock?
A separate point… since a major use case is CLIs, we should keep in mind, there are a number of libraries for command-line argument parsing, and any proposal should either work with them well, and/or remove or reduce the need (like Ammonite’s approach).
What happens if I explicitly define both program X and object X? Is the main method inserted in the same way apply and unapply work with case classes?
I tend to agree with @Ichoran that the minor convenience doesn’t pay for the cognitive cost of learning a new construct and new desugaring rule. If there were a way to plug in different runtimes it might be more compelling; i.e., something like
// Default runtime, takes Array[String] => Unit
program MyApp { args => ... }
But even so there are 95k hits for "program" in Scala projects on GH and at least some of these are identifiers that will become syntax errors if we introduce a new keyword. I’m not convinced it’s worth it.
I agree with @odersky that the = syntax would probably misleading for new users. It makes sense to me to think of program as more like object and less like an assignment.
You can make the argument that the main method is a bad idea and it should not be there. But it will always be there. You cannot get rid of it, you can only hide it. Application, App or any of the proposals here do not remove the main method, they only hide it under a layer of sugar.
If you look at the stack trace, or use reflection, you will still see the main method. You can call it, if you like. You may collide with it if you try to define another member of the same name.
If you use a build tool, it might ask you which main method you want to run. SBT has a command “run-main”).
This whole discussion is not about whether there should be a main method. There always will be. The only question is how much sugar you want on top of it.
**Welcome to Scala 2.12.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_191).
Type in expressions for evaluation. Or try :help.
object Hello extends App { println(“Yo!”) }
defined object Hello
Hello.main(Array.empty)
Yo!
object No extends App { def main(args: Array[String]): Unit = println(“Oh, no!”) }
:11: error: overriding method main in trait App of type (args: Array[String])Unit;
method main needs `override’ modifier
object No extends App { def main(args: Array[String]): Unit = println(“Oh, no!”) }
^**
Agree. We can even rename App2 to Program, so we get something closer to the proposal of OP:
class Program(entryPoint: => Unit){
def main(args: Array[String]): Unit = entryPoint
}
object Boo extends Program({
println("Hello World")
})
If we want args then that’s still easily doable:
class Program(entryPoint: => Unit){
private var _args: Array[String] = null
final def args: Array[String] = _args
final def main(args: Array[String]): Unit = {
_args = args
entryPoint
}
}
object Boo extends Program({
println(s"Hello World! Args = $args")
})
That’s a flexible solution which doesn’t require any special handling by compiler, doesn’t have any nasty surprises, doesn’t lead to deadlock on loading main class and can be easily extended to support any pre- and post- entryPoint actions. It’s also concise enough it shouldn’t obfuscate small but complete example programs.
Update:
Above example with args doesn’t work. I’ll post fixed version below.
It doesn’t work, as args is not in scope in your example. But that’s easily fixed with an implicit function type (as in my example).
I think this shows we really do not need a special language feature for that.
But I’d still like to see the zero-syntax .sc approach, which is already used in practice, be formally made the standard way of defining Scala application entry points.
The class Program whose constructor takes a by-name or an args => body lambda is clever as far as boilerplate reduction goes, but does not help with one of the core concerns: how do we teach a beginner how to write their hello world. It’s even more difficult to explain the block inside the parentheses of the super constructor call than it is to explain the def main(args: Array[String]): Unit = { ... }.
Hi, chiming in just to say that if you’re going down the road of special-casing this, also consider the impact it has downstream to anyone who’s not a beginner.
Introducing a program keyword may not be a breaking change for the language (as explained in the proposal), but it’s a breaking change for any tool that needs to parse and understand Scala code (formatters, linters, IDEs, not to mention any other custom tools companies may have).
I tend to agree that this proposal seems a tad too biased towards new developers and the teaching aspects.
Also, while beginners who are just starting programming may find the new syntax appealing, I think this proposal will actively make things more confusing for developers who have experience in another language where having a main method is normal (Java and C, for instance).
To sum it up, considering the (imo negative) impact a new language “shortcut” will have on:
existing Scala developers
existing developers coming for languages where main is normal and expected
existing tools and tooling authors
I don’t think it would be a worthwhile addition to the language.