Sunday, June 07, 2009

Scala on .net — not ready for serious use...

...at least with Scala 2.7.3, and what it gets for sbaz scala-msil in any case.

Having written (well, ported) a moderate amount of Scala code over the last few weeks, towards an experiment in cross-VM code (using my tidied up version of the C# port of the to-Erlang jinterface library as the template), I thought it was time to start with the serious porting experiment.

Have you ever read the Arabian Nights (like the full thing e.g. the Penguin Classics version)?

The format goes roughly

def Quest(p : Problem) : Resolution = {
  val helper = Helper.WiseMan(p)
  helper.Ask(p) match {
     case p2 : Some[Problem] => helper.Resolve (
               Quest(p2.get()) // almost always this branch
               )
     case None => helper.Resolve()
  }
}

and so it feels with this task.

The obvious way to model the threading and mailbox code in the library is to use the scala.actors library. But that's not in predef.dll -- the source is part Java, part Scala. Noting that Scala 2.7.5 has some fixes for actors, I start with that code base.

Step 1 -- use the Java conversion assistant, build as C#, fixing up all the missing bits, then try to build the Scala code against it. It reports an abject failure to find the generated equivalent of Runnable.

Step 2 -- move the classes in SupportClass.cs into the scala.actors namespace, and rebuild. The shows up all the Java classes used in the Scala code, but also yields up the assertion that the FJTask class is broken:

4@(06 1f 09 02)
error: error while loading FJTask, type 'scala.actors.FJTask' is broken
(1f@2 in (06 1f 09 02))

-- I've not looked to what the IL means in this context. This happened even when compiling the C# with the .net 1.0 compiler.

Step 3 -- bite the bullet and port the Java code to Scala as a standalone project, with .net system calls. Fairly simple to do. Get all the syntactical errors out and the compilation throws an exception trying to do something with Object.wait()

Cannot find method class Object::wait
scope = {
  def this(): System.Object;
  protected def finalize(): Unit;
  def hashCode(): Int;
  def toString(): System.String;
  protected def MemberwiseClone(): System.Object;
  def GetType(): System.Type;
  def equals(System.Object): Boolean;
  final def ==(System.Object): Boolean;
  final def !=(System.Object): Boolean;
  final def eq(System.Object): Boolean;
  final def ne(System.Object): Boolean;
  final def synchronized(System.Object): System.Object;
  final def $isInstanceOf[T0 >: ? <: ?](): Boolean;
  final def $asInstanceOf[T0 >: ? <: ?](): T0;
  def clone(): System.Object;
  def wait(): Unit;
  def wait(Long): Unit;
  def wait(Long,Int): Unit;
  def notify(): Unit;
  def notifyAll(): Unit
}
Exception in thread "main" java.lang.Error: System.Object.wait
        at scala.tools.nsc.backend.msil.GenMSIL$BytecodeGenerator.scala$tools$nsc$backend$msil$GenMSIL$BytecodeGenerator$$getMethod(GenMSIL.scala:2373)

-- and that's after I've replaced all the instances of such a call with Monitor.Wait(), and the same for Object.notify() going to Monitor.Pulse(). But in order to get this far, I had to remove every instance of private[package-name]; and while the @volatile attribute seemed to be accepted as syntax, the emitted MSIL did not honour this decoration.

Pretty much at a dead end here.

Out of curiosity, I tried building the Erlang-bridge library I was originally intending to build for both platforms. After a little bit of tidying away calls to (java.lang.)String.format(), the compilation throws again, this time complaining about trait Projection in the context of the DigitsArray helper class (which HAS A rather than IS A Array[Int]) for immutable BigIntegers (intended to substitute for the lack of same on .net, at least this side of .net4). And this with code that passes unit tests with 90%+ coverage on the JVM.

class DigitsArray
  symbol = final class DigitsArray
  owner  = final <module> <package> <java> package platform
with methods = List(com.ravnaandtines.platform.DigitsArray.<init>, com.ravnaandt
ines.platform.DigitsArray.ShiftLeft, com.ravnaandtines.platform.DigitsArray.Shif
tRight, com.ravnaandtines.platform.DigitsArray.FreeBits, com.ravnaandtines.platf
orm.DigitsArray.getSlack, com.ravnaandtines.platform.DigitsArray.tailCountNegati
ve, com.ravnaandtines.platform.DigitsArray.GetDataUsed, com.ravnaandtines.platfo
rm.DigitsArray.ResetDataUsed, com.ravnaandtines.platform.DigitsArray.length, com
.ravnaandtines.platform.DigitsArray.update, com.ravnaandtines.platform.DigitsArr
ay.apply, com.ravnaandtines.platform.DigitsArray.DataUsed, com.ravnaandtines.pla
tform.DigitsArray.IsZero, com.ravnaandtines.platform.DigitsArray.IsNegative, com
.ravnaandtines.platform.DigitsArray.toString, com.ravnaandtines.platform.DigitsA
rray.hashCode, com.ravnaandtines.platform.DigitsArray.equals, com.ravnaandtines.
platform.DigitsArray.AsIterator, com.ravnaandtines.platform.DigitsArray.AsSeq, c
om.ravnaandtines.platform.DigitsArray.lead, com.ravnaandtines.platform.DigitsArr
ay.consistent, com.ravnaandtines.platform.DigitsArray.<init>, com.ravnaandtines.
platform.DigitsArray.<init>, com.ravnaandtines.platform.DigitsArray.<init>, com.
ravnaandtines.platform.DigitsArray.<init>, com.ravnaandtines.platform.DigitsArra
y.com$ravnaandtines$platform$DigitsArray$$data, com.ravnaandtines.platform.Digit
sArray.dataUsed_$eq, com.ravnaandtines.platform.DigitsArray.dataUsed, com.ravnaa
ndtines.platform.DigitsArray.$tag)
Exception in thread "main" java.lang.Error: trait Projection
  symbol = abstract <interface> <trait> trait Projection
  owner  = final <module> object Array with name scala.Array.Projection
        at scala.tools.nsc.backend.msil.GenMSIL$BytecodeGenerator.getType(GenMSI
L.scala:1987)

Reality check -- redo last year's cross-platform experiments ("hello world", and a Python/Scala stack). They still work just fine.


If the compiler worked, then it would be quite simple to port the Actors library. There would need to be some alternate code for the Debug.scala and FJTaskScheduler2.scala calls to java.lang.System; and applying some consistency to whether java.lang is explicitly or implicitly imported (simplest if always implicit, for the awkwardly inconsistent use of imports for Runnable and InterruptedException. Working around the fact that System.Threading.Thread is sealed is fairly easy.

I tried this, and the exception I get, with just stubs for the Java classes is

object Eval$2
  symbol = final case <module> object Eval$2
  owner  = final <module> object Futures
with methods = List(scala.actors.Futures.Eval$2.<init>, scala.actors.Futures.Eva
l$2.readResolve, scala.actors.Futures.Eval$2.productElement, scala.actors.Future
s.Eval$2.productArity, scala.actors.Futures.Eval$2.productPrefix, scala.actors.F
utures.Eval$2.toString, scala.actors.Futures.Eval$2.$tag)
Exception in thread "main" java.lang.RuntimeException
        at ch.epfl.lamp.compiler.msil.emit.ILGenerator.emit(ILGenerator.scala:48
6)

which refers to unmodifed code from the Scala actors library!

As it is, after the first easy side-quest to get the Actors library, the next side-quest would be the serious work of extending the compiler-to-MSIL. Alas, compilers are one of those bits of the field that I've never dabbled in before. So that'd be the next level of side-quest...

Other people ("Hi, Ivan!") seem to have gotten as far as the "Hello world!" stage too, but I've not seen any other reports of anything serious being attempted.

Which is a pity, since I quite like the language -- it's not as spiky as Erlang or as gnomic as F#, while allowing you functional style goodness, even if it is not fully a functional language in the truest sense (everything is an object, even functions).


Later -- test cases uploaded to my Mediafire WorksInProgress folder; the various build.bat files in scala-actors.net.7jun09.7z provoke different crashes.

No comments :