0

I am learning Scala and trying to write some command line executables.

I have two version of HelloWorld, which I thought were semantically the same. HelloWorld.scala compiles and runs successfully from the command line. HelloWorld2.scala compiles but produces a runtime error.

My Question: I would think that the two would be semantically the same, so why does the second one produce a runtime error?

Here's the working example:

// HelloWorld.scala

object HelloWorld {

  def main(args: Array[String]): Unit = {
    println("Hello, World!")
  }
}

Here's the broken example:

// HelloWorld2.scala

object HelloWorld2 {

  def main
    : Array[String] => Unit
    = args          => {
      println("Hello, World!")
    }
}

Here's the console output:

java.lang.NoSuchMethodException: HelloWorld2.main([Ljava.lang.String;)
    at java.lang.Class.getMethod(Class.java:1778)
    at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:66)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
    at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
    at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:65)
    at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
Fried Brice
  • 769
  • 7
  • 20
  • 1
    It is looking for `HelloWorld2.main` but you only have `HelloWorld.main`. Try either changing the class name to match the file, or changing the file name to match the class (i.e. copy the code in your broken example into HelloWord.scala) – The Dark Jul 25 '16 at 05:00
  • 1
    You have two different things here: a `main` method which takes an `Array[String]` and has return type `Unit`, versus a `main` method which takes no arguments and returns a function that takes an `Array[String]` and has return type `Unit`. – Jesper Jul 25 '16 at 07:25
  • @TheDark, Thank you for pointing out the object name. TBH, that was just a typo in my question: the actual files I was working with named the object correctly. – Fried Brice Jul 25 '16 at 19:50
  • @Jesper, I had never in my wildest imagination considered that a method and a function where not the same thing ^_^; – Fried Brice Jul 25 '16 at 19:52

1 Answers1

5

Regardless that scala can convert a method to a function (with eta-expansion, sometimes automatical), they're different things here. The major difference is that scala generates a different bytecode for JVM.

Talking about your example, you actually defined a method def that returns an object of class Function1:

def main: Function1[Array[String], Unit] //you could even put `val` here

when JVM requires a method with completely different signature:

def main(args: Array[String]): Unit

Because for JVM a function is just an instance of a class FunctionN, scala-compiler doesn't convert it to a method automatically. Manual conversion would look like:

def main(args: Array[String]): Unit = main.apply(args)

def main: Array[String] => Unit = ...// or `val main`

Note that apply is just a method of Function1 class, () is just a syntax sugar for calling apply.

Update:

Just an additional information. As @som-snytt pointed out, scalas runner is more flexible about main's signature, so this:

def main(args: Array[String]): Int

will work for scala HelloWorld, but won't work for java HelloWorld - it will require Unit (void) as a return type. I can also recall some differences between compiling Java-code with javac vs scalac. So the point is that scala/scalac is evolving, so it might be possible (in future) for scala to run more relaxed main methods, like maybe functional interfaces or something. It can also compile and run scripts, btw.

Graham
  • 7,431
  • 18
  • 59
  • 84
dk14
  • 22,206
  • 4
  • 51
  • 88
  • 1
    Just `java` requires that sig. Define `def main(args: Array[String]): Int` and you'll see a stern warning, but `scala` will run it. Scala could be much more flexible. It would be nice if it printed the result value by default. Also, thx for the doc link. – som-snytt Jul 25 '16 at 06:32
  • Thank you very much for the spot-on answer and clear explanation. I've been trying to write my Scala decidedly un-idiomatically, writing out everything's type signature on a separate line. Unfortunately, it seems I'll have to stop doing that. – Fried Brice Jul 25 '16 at 19:53