Sunday, January 20, 2013

Scala weirdness


This one almost cost me a weeks work, and you are going to love it - maybe obvious to Scala practitioners - but definitely unintutive to someone who is just using Scala : like me.


Consider a class

class MyClass(private var field: Int) {
  private def processField(value: Int) {
    // For now, assume it is just setting it after some processing
    this.field = value
  }
}


Do a javap on it, and we get :

> $JAVA_HOME/bin/javap -p  -cp . MyClass
Compiled from "MyClass.scala"
public class MyClass implements scala.ScalaObject {
  private int field;
  private int field();
  private void field_$eq(int);
  private void processField(int);
  public MyClass(int);
}


Which is fine - our method processField exists : with same signature as we would expect if we had written it in java.


Now, let us add an object MyClass - which can directly invoke this processField. For completeness sake, I am including the modified .scala file below :

class MyClass(private var field: Int) {
  private def processField(value: Int) {
    // For now, assume it is just setting it after some processing
    this.field = value
  }
}

object MyClass {
  def someMethod(inst: MyClass, value: Int) {
    inst.processField(value)
  }

}

Running scalac on this causes no errors - as expected : the object can invoke private methods on MyClass instance - as scala defines.
But now, there is a non trivial change to the generated class. Notice it below.

> $JAVA_HOME/bin/javap -p  -cp . MyClass
Compiled from "MyClass.scala"
public class MyClass implements scala.ScalaObject {
  private int field;
  public static final void someMethod(MyClass, int);
  private int field();
  private void field_$eq(int);
  public final void MyClass$$processField(int);
  public MyClass(int);
}


Yep, the processField method is now NOT what we defined :
a) It has been renamed with namespace prefix.
b) It is now PUBLIC ! Yes, a private method has its access permission changed !

(b) is a very severe issue : and probably a bug IMO and (a) is what hit me as a issue



Now you ask, why all the fuss about all this ? After all, the names are private to scala and invocations to them work as expected.

Yes and no - when you are interfacing java with scala, things will fail.
And more importantly, when you are interfacing scala with java - explicitly or implicitly, things can fail.

The last is what happened to me - one word : SERIALIZATION.

I was invoking readObject/writeObject from the object - which, from a java background, is common practise when you have custom serialization (to invoke from a static method).

Here, it fails spectacularly - since the method signatures are royally mangled by scala.


So the dirty workaround ?

- Add all the actual logic within readObjectImpl and writeObjectImpl
- readObject and writeObject delegate to the Impl methods.
- object invocations are on the Impl and NOT on read/write Object

This preserves the method signature while ensuring things work.
I found this out the hard way - I hope there is an easier solution, but I did not find it.


In the hope that someone else might not trip over this issue and waste days, posting it here !



1 Comments:

Anonymous Anonymous said...

Just saw this. Are you still doing the chess engine development?

Daneil Shroff

6/18/2013 04:39:00 AM  

Post a Comment

Links to this post:

Create a Link

<< Home