Friday, May 01, 2009

Scala's main issue

From Chris Gioran: For those arriving here from googling up this issue, I would like to point out that it has been fixed @2.8.0
Now main methods defined in companion objects are generated static and will work as expected. The ticket has also been closed as fixed.

I love Scala, I really do and I think its the next big thing. My main issue with the language is its unclear and sometimes misleading compiler errors.

My working practice in learning new technology, and I think its a common case, is to read a bit about it and then try it out. If something goes wrong then google about it using the error message, read a bit more, and master it as I go. It means error messages are actually as important as (and maybe more then) the documentation, especially for beginners.

Since Scala is a relatively young and very rich language, there are not enough writing on the web about its rough spots. In that light it is even more important to provide clear documentation about these spots. To be sure, Scala has its share of rough spots and I don't mean by that that other languages has less of them. To paraphrase on Robert Glass "Scala is a very bad language, but all the others are so much worse".

Now to the point. I was trying to write this very simple Scala app with a main method:
package test1
import test1.java.SuperTest

class Test extends SuperTest{
def useSuper {
print(superMethod())
}
}

object Test{
def main(args: Array[String]): Unit = {
val t = new Test
print(t.useSuper)
}
}
And tried to run the main method on it but got
java.lang.NoSuchMethodException: test1.Test.main([Ljava.lang.String;)
. Ok, I said, I know that the companion object is compiled to Test$ and the main method
is defined in the object, not in the class. So I tried to run Test$ but then I got
main() method in test1.Test$ is not declared static
. Checked the class and found that its true:
$ javap build/test1/Test$
Compiled from "Test.scala"
public final class test1.Test$ extends java.lang.Object implements scala.ScalaObject{
public static final test1.Test$ MODULE$;
public static {};
public test1.Test$();
public void main(java.lang.String[]);
public int $tag()       throws java.rmi.RemoteException;
}
Wow! what's going on here? All the code example I found claim that that's the way to go. After banging my head a bit I removed the class definition and left the object to be a plain object (not a companion object anymore), and it worked! It means that there is an undocumented side effect to having a companion (as all of us married folks know). After figuring out the solution I googled a bit more and found that its a bug and it might be solved in Scala 2.8.0. But since its open for more then 16 months now, and the compiler knows its an edge case I think this kind of issue should have a special compiler warning.

8 comments:

Brian Burg May 1, 2009 at 11:16 PM  

I suppose such a warning would signal defeat, but I think this is a edge-case design issue. It affects several valid use cases beyond main methods+companion objects, but I believe those who enjoy solving design problems are drowning in the tasks of getting new features nailed down for 2.8.

Eishay Smith May 1, 2009 at 11:53 PM  

Hi Brian
> I suppose such a warning would signal defeat
Masking bugs and frustrating developers is even worse.
> It affects several valid use cases beyond main methods+companion objects
Right, see the next post :-)

Ismael Juma May 2, 2009 at 12:30 AM  

H Eishay,

Yes, this is annoying and hopefully it will be fixed.

For what is worth, the fact that forwarders are not added automatically for companion objects is actually mentioned in Programming in Scala with the reasoning.

Also, it's worth mentioning that a fix had been committed but it caused other issues and it was reverted, so it's not that no-one looked at it for 16 months.

Finally, there's also http://lampsvn.epfl.ch/trac/scala/ticket/1735 which is related.

Best,
Ismael

Eishay Smith May 2, 2009 at 8:52 AM  

>For what is worth, the fact that forwarders are not added automatically for companion objects is actually mentioned in Programming in Scala with the reasoning.

Can you please point to the chapter/page its mentioned in?
Most developers seldom write a main method but still, I would hope the compiler would hint the developer on a potential problem.

> Finally, there's also http://lampsvn.epfl.ch/trac/scala/ticket/1735 which is related.
Man, you are a a knowledge goldmine :-)

Ismael Juma May 2, 2009 at 10:35 AM  

It's in the "Combining Scala and Java" chapter. I had a quick look in the PDFs and it seems like more recent versions don't have the reasoning anymore, just the explanation of the translation.

The relevant quote from the PrePrint edition is:

"This difference in behavior is due to a restriction in Java’s class file for-
mat: The JVM does not let you define a static method with the same name
and signature as an instance method in the same class. On the other hand,
Scala imposes no such restriction: A class can well define a method with the
same name and signature as its companion object. Therefore, it would not
be safe to install forwarders for all static methods, because they might clash
with an instance method of the companion class."

In any case, it should be possible to only add the forwarders if there are no clashes and I believe that is the current aim (implementation difficulties aside).

Hope that helps,
Ismael

Kevin May 27, 2009 at 10:37 PM  

I found the blog googling for exactly this problem. I'm wondering what the standard practice is for writing a main method to exercise some data type. As Eishay says, writing a main method isn't that common, but the one time it is common is when learning a new language and just playing around with it.

Chris Gioran September 3, 2010 at 7:02 AM  

Hi,

for those arriving here from googling up this issue, I would like to point out that it has been fixed @2.8.0
Now main methods defined in companion objects are generated static and will work as expected. The ticket has also been closed as fixed.

Eishay Smith September 3, 2010 at 8:24 AM  

Thanks Chris, I'll update it in the post

Creative Commons License This work by Eishay Smith is licensed under a Creative Commons Attribution 3.0 Unported License.