Pre-SIP: program Foo = { println("Hello world!") }

@odersky basically everything you’ve described with @main methods has been implemented in Ammonite scripts. It works very well!

Excellent! So let’s standardise on it.

Haha, that’s what Proposal: Simplifying the Scala getting started experience - #32 by shawjef3 is all about :stuck_out_tongue:

Am I forced to make a @main def? I’m worried that we are just shifting boilerplate around the plate, if you see what I mean.

@main def myMain(...) = { // boilerplate
... my
... script
... goes
... here
...
... indented?!?
} // boilerplate that I'm likely to get wrong, delete by accident, have to explain to friends

We could instead use an inversion of control to inject the (parsed) arguments part way through the script.

someCode
val (some: String, params: Int, go: Double, here: Option[Char]) = @args
moreCode

Sorry to be so bikesheddy about this, but I really strongly believe that getting this right is really worth getting right.

Yes you are forced to define a function if you want to take arguments with automatic deserialization. I agree with you that this isn’t the place for bikeshedding that specific a detail

If we allow top-level statements they would presumably run before the @main method.

I’m not sure how much of an advantage there is to running a compiled top level as an entry point, especially if it would not have access to the command line arguments (and I agree that the top level should not, except for scripts, but that we already have; this is about compiled code).

A compiled toplevel code in a .scala file would not contain arbitrary statements, only definitions.

I understand (although it was floated in the thread about top-level definitions – I discussed the argument against it there). My point was it’s not realistic to expect less ceremony than the @main def... approach. If we would (as Matthew implied) propose to make the entry point top level, that would imply allowing top-level statements, which would address his concern about the indentation, but at that point you don’t need the top level to be the entry point.

In other words, I’m suggesting his case is not a case for making the entry point top level, even if it remains an argument in favor of supporting top level statements (which indeed is not currently on the table).

@lihaoyi Can you point me to the docs for the rules for when a method can be a legal @main in Ammonite and how arguments are converted? It would be good to arrive at some harmonization between script code and normal code for this.

Whole documentation about @main is in this paragraphs: https://ammonite.io/#ScriptArguments and https://ammonite.io/#MultipleMainMethods
I could not find documentation about parsing arguments but quick test shows something:

//file: test.sc
case class Ex(s:String)
@main def main2(v:Int = 2, t:Ex) = println(v)

//run it with `amm test.sc 2`
//test.sc:5: could not find implicit value for evidence parameter of type scopt.Read[ammonite.$file.test.Ex]
//@main def main2(v:Int = 2, t:Ex) = println(v)
//          ^
//Compilation Failed

currently scopt.Read[T] is used.

Only top-level functions in scripts are allowed to be annotated as @main. As far as behavior is concerned, the documentation is pretty thorough, and you can play around with it yourself if you want more detail.

Apart from Ammonite, the same function-to-entrypoint logic is also used in defining Mill commands, and Cask HTTP endpoints (though using a different typeclass for deserialization). It works very, very well

I believe you :smile:. But to put this into the main compiler we need to spec it, and for that we need some wordings for rules. Also, we can’t have any dependencies (e.g. to scopt).

Why do we need to put it into the main compiler though? For getting-started level usage using it in Ammonite is enough, and for usage in larger projects built using Mill or SBT, it could easily be a third party library (using Macros in Scala 2.x, no idea about Dotty)

Even if standardized, if it can live outside the compiler codebase, it probably should live outside the compiler codebase. It’s not like newbies start using scala by reading the spec and downloading scala-compiler.jar

In this particular case, there is enough detail that goes into making @main work that it would be strange to have it all in the language spec. Maybe in the standard library, at most, but speccing things like the autogenerated help or error messages or the argument parsing implementation definitely seems like too-much-information

Thinking a bit further, there is definitely a decorator-like pattern underlying the Ammonite/Mill/Cask implementations of entrypoint functions that could conceivably become a language feature. However, extracting that out, defining it, and making sure it’s extensible in all the right ways, is a deep discussion in its own right and probably out of scope for a discussion based around simplifying main methods and getting started for newbies.

Proposal: Simplifying the Scala getting started experience lays out what I think we should do to help beginners. I don’t think it makes sense to discuss @main method language additions and specs in the abstract without the proper context of how it fits into a larger strategy.