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)
4
  • 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) Commented Jul 25, 2016 at 5: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. Commented Jul 25, 2016 at 7: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. Commented Jul 25, 2016 at 19:50
  • @Jesper, I had never in my wildest imagination considered that a method and a function where not the same thing ^_^; Commented Jul 25, 2016 at 19:52

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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.
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.