Skip to content

Scala 3 generated @main function is generated in package scope #21348

Open
@ekrich

Description

@ekrich

Compiler version

Scala 3

Minimized code

Suppose you are converting a Scala 2 application that has 2 main methods in the same package. This is not that uncommon as you may put applications in a app package. Here is the code, put in the same file for simplicity for the issue.

package app

object Foo {
    def main(args: Array[String]): Unit = println("Foo")
}

object Bar {
  def main(args: Array[String]): Unit = println("Bar")
}

Now you are converting this Scala 3 or just happen to want to add another application and you like the name main for your entry point as you are used to that convention. You convert the args using the documentation as you would get an conversion error No given instance ... if you left it as an Array[String].

package app

object Foo {
    @main def main(args: String*): Unit = println("Foo")
}

object Bar {
  @main def main(args: String*): Unit = println("Bar")
}

Output

Now this second example doesn't compile with a main is already defined as main ... error pointing to one or the other application that was compiled first.

Expectation

Generated or synthetic code should be scoped to the scope it is found in. It seems weird that code found in object scope is generated in package scope. This may be a more advanced way of thinking about this. The overall point is this is really surprising and bound to catch people because of the overall ergonomics.

There is also an additional surprise as follows:

package baz

object Bar {
  @main def main(): Unit = println("main Bar")
  @main def bar(): Unit = println("bar Bar")
}

Results in a warning (maybe for case insensitive file systems or something).

warn] 9 |object Bar {
[warn]   |       ^
[warn]   |Not generating the static forwarders of Top(baz.Bar) because its name differs only in case from the name of another class or trait in this compilation unit.

Note that main functions are not really special in Java except for the signature. They can be called by other code like a normal function.

I know the intent was to try and make entry points easier, nicer, and more flexible with less boiler plate. These sort of "edge" cases or just stuff people may try to do will be very unexpected.

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions