My hobbyist coding updates and releases as the mysterious "Mr. Tines"

Saturday, 29 November 2008

Python Swings; Ruby still doesn't

Here's a proof-of-concept of running a Swing UI from Python on the CLR and the JVM, following up on earlier experiments (which I had forgotten about).

try:  
  # IronPython must load the J# redistributable libraries  
  # Using the FullName of the assemblies is probably overkill  
  import clr  
  clr.AddReference( 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )  
  clr.AddReference( 'vjssupuilib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )  
except ImportError:  
  pass  
     
import javax.swing  
import java.awt.event  
import java.lang  
  
class Exit(java.awt.event.WindowAdapter):  
  def windowClosing(self, evt):  
    evt.getWindow().setVisible(False)
    evt.getWindow().dispose()
  def windowClosed(self, evt):  
    java.lang.System.exit(0) 
    
# With the 'import's above, we can now refer to things that are part of the  
# standard Java platform via their full paths.  
frame = javax.swing.JFrame("Window") # Creating a Java JFrame.  
label = javax.swing.JLabel("Hello")  
     
# We can transparently call Java methods on Java objects, just as if they were defined in Python.  
frame.getContentPane().add(label)  # Invoking the Java method 'getContentPane'.  
frame.addWindowListener(Exit()) # Need to do this for IronPython/J# which is barely JDK 1.2 level  
frame.pack()  
frame.setVisible(True)

which works happily with java -jar jython-complete.jar HelloSwing.py using Jython 2.5β0 and with ipy.exe HelloSwing.py for IronPython2.0RC2 as well.

I was briefly conflicted earlier this week when I saw that IronRuby had reached α2, and wondered about a JRuby/IronRuby UI, but the nigh-equivalent

begin  
  require 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'  
  require 'vjssupuilib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'  
rescue LoadError  
  # This is the 'magical Java require line'.  
  require 'java'  
end    
        
class Exit < java.awt.event.WindowAdapter
  def windowClosing evt
    evt.getWindow.setVisible(false)
    evt.getWindow.dispose # make Closed
  end
  def windowClosed evt
    java.lang.System.exit 0 
  end
end

# With the 'require' above, we can now refer to things that are part of the  
# standard Java platform via their full paths.  
frame = javax.swing.JFrame.new("Window") # Creating a Java JFrame.  
label = javax.swing.JLabel.new("Hello")  
     
# We can transparently call Java methods on Java objects, just as if they were defined in Ruby.  
frame.getContentPane.add(label)  # Invoking the Java method 'getContentPane'.  
frame.addWindowListener(Exit.new()) # Need to do this for IronRuby/J# which is barely JDK 1.2 level  
frame.pack  
frame.setVisible(true)  

which works on JRuby 1.1.5, fails in IronRuby α2 when trying to load Swing classes, and I've not found a dodge that will get me at even class metadata for the Swing files. Ruby's casing rules may make it impossible for IronRuby to recognise the lower-case Java namespaces (I had fun with that earlier with Ruby.Net).

LATER: I could do something VM dependent that takes the class name and argument list for construction in {Iron|J}Ruby. What is more tricky is inheritance like

class Exit < java.awt.event.WindowAdapter

which is a major use-case. I wonder if there might be a metaprogramming technique I can use for this...

LATER YET: The problem is that there is no exposed mechanism to wrap a System.Type as a Ruby Class; I could manually subclass everything that is not final that I want in a C# assembly with a My.JavaNamespace and emulate the way that JRuby maps namespace Java::JavaAwtEvent to a value named java.awt.event, but the nearest to automation I can see is to Reflection.Emit a whole ton of stuff.

So, for the moment, consider it a dead end.

Sunday, 19 October 2008

Sign of the times -- Impedence mismatch of Scala and J#

Following up on the Scala/.Net experiments, I took the interfaces for hashes and cyphers from my Java port; and replaced them in the Java world with a set of Scala traits, like:

trait MDA
{
    def update(data:Array[byte], offset:int, length:int): unit

    def update(data:byte) : unit

    def digest() : Array[byte] 
}

and it builds happily (though NetBeans 6.5 nightly complains it can't find the interface definition from the library in the code view).

Over to Visual Studio, with a suitable script to build the equivalent .dll -- and the implementing classes in J# fail to build because the Array[byte] maps to ubyte[] while a J# byte[] is an sbyte[], and so the J# compilation fails to match the signature of the Scala.Net trait -- and there's no Java ubyte type I can force everything to. And as byte arrays -- or at least bytes -- are the fundamental building block for streams, it looks like all the Java code has to get the elbow.

Thursday, 25 September 2008

Why am I doing this sort of thing?

Michael Foord (aka Fuzzyman) has wondered, while linking me, about why one would want to talk to Scala from IronPython.

Partly, it is the intellectual challenge of that particular sub-problem. But the broader goal is to see how far it is possible, in this polyglot age, to build a language stack -- along the lines the Ola Bini mused about at the start of the year -- that one could drop onto the JVM or .Net using essentially the same code base, give or take a thin C# layer to provide compatible APIs in .Net to those needed in Java but not present in J#.

While the Fan language is developed to be exactly so code-compatible across the JVM and CLR, it appears to be resolutely monoglot, with the platform hidden behind an abstraction layer.

The ulterior motive is to take a codebase that I already have in Java/J#, the first stages of a reimplementation in a VM targeted language, of my old CTClib project -- and be able to complete the reimplementation/upgrade to RFC2440 spec, in a platform neutral way, and without having to write any more Java, even if the IronPython/J#+Swing interface will be rather less Pythonic than I could manage if I were just doing it in Jython.

And why Scala as well? For any new static layer code, especially where being explicit about the types involved, such as around the crypto, will make things clearer.

One correction, though, to the remarks : ATM, I'm using the scala-net compiler, which is part of the Scala bazaar, and seems (from its failure modes when the J# libraries are in the loop) to be a different generator to that in IKVM. Not being IKVM means I'm also spared having to drag in the GNU Classpath library, where the server stuff has priority, and desktop UI support is still primitive -- its AWT is less functional, in many important respects, than Java 1.0 was.

Wednesday, 24 September 2008

Scaling the tower of Babel II

Finally, a little digital extraction. Scala from IronPython, works as expected. In file operator.scala:

package com_ravnaandtines.operator

trait Process {
 def op(x:Int, y:Int) : Int
}

class Multiply extends Process {
  def op(x:Int, y:Int) : Int =
   x * y
}

class Perform69 {
 def sixByNine(x: Process) =
  x.op(6,9)
}

Set up as in the previous post, and build using %SCALA_HOME%\bin\scalac-net.bat operator.scala to get operator.msil -- no assembly is generated at this point. Edit the .msil file as required to point at the appropriate versions of the runtime (see previous post). Then ilasm .\Operator.msil /dll /output=operator.dll. Now fire up IronPython:

>>> import clr
>>> clr.AddReference("operator.dll")
>>> from com_ravnaandtines.operator import *
>>> dir()
['Multiply', 'Perform69', 'Process', '__builtins__', '__doc__', '__name__', 'clr']
>>> m = Multiply()
>>> p69 = Perform69()
>>> p69.sixByNine(m)
54
>>> class Adder(Process):
...   def op(self,a,b):
...     return a+b
...
>>> add = Adder()
>>> p69.sixByNine(add)
15
>>>

So long as we get our interfaces/traits from Scala, all is fine.

In order to do the same in the JVM world, I need to have done set CLASSPATH=%CLASSPATH%;C:\scala-2.7.1.final\lib\scala-library.jar; and then I can do exactly as above, but without the first two lines with clr. Those I can put in a try/except, for common coding.

Note -- it is neither necessary nor sufficient to put the Scala library jar in java.class.path or python.path properties.

Saturday, 23 August 2008

Scaling the tower of Babel

A few months ago, I was playing around with seeing how I could adapt an existing Java code base so that I could finish doing the work in better languages -- and have the code work on both the JVM and the CLR.

It seems that frustration will inevitably follow any attempt to incorporate any J# into the .Net versions of the stack (both IKVM and the Scala-net compilers bork when they see another Java in the stack) -- even though you can call Jython into Scala into Java on the JVM, this Tower of Babel cannot stand on the CLR with the J# runtime in the picture -- Java e.g. a Swing-based GUI using the J# extensions, has to stand off to one side.

That said, the Scala on .Net "hello world" with Scala 2.7.1 is comparatively painless.

  1. Install the scala-msil package (sbaz install scala-msil)
  2. Create file Hello.scala as
    import System.Console
    
    object test extends Application {
      Console.WriteLine("Hello world!")
    }
  3. In a Visual Studio command prompt, issue %SCALA_HOME%\bin\scalac-net.bat hello.scala
  4. Copy %SCALA_HOME%\lib\*.dll to the current directory
  5. Run the resulting test.exe

This uses the 1.0 version of mscorlib.dll; as a post-processing step you may wish to re-base against the .Net 2.0 version -- for this, you need to rebuild predef.dll and scalaruntime.dll using the same technique as needed to strong-name them after the fact; only this time you want to reset the mscorlib reference from

.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 1:0:5000:0
}
to
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}

as well as doing any strong-naming you may desire.

To set the assembly name, add -Xassem [assembly name] to the compile command, to generate hello.exe.

To control the type of the generated assembly (.dll rather than .exe), I've not found a way other than taking the generated [assembly name].msil and ilasming it manually. In particular trying to skip generating the unwanted .exe will mean figuring why using scalac.bat -target:msil always gives

error: fatal error: class scala.runtime.BoxedBooleanArray not found.

whatever I do with -Xassem-path or tweaking environment variables.

Friday, 8 August 2008

Integrating .Net with Erlang, part 8

Gratuitous update on 08/08/08 with a small increment of unit testing -- now using Rhino Mocks 3.4 as well as home-grown ones, and NUnit 2.5 (alpha 3) for the neat

Assert.That( [delegate expression], Throws.Exception<[type]>())
syntax.

Drop here, as usual.

Saturday, 7 June 2008

Erlang and Python

Following the suggestion that I try TwOtp, I've tried the "kicking the tyres" with that library.

Adapting the sample code to work against my own local node seemed easy enough:

from twisted.internet import reactor
from erlang import OneShotPortMapperFactory, buildNodeName

def gotConnection(inst):
    return inst.factory.callRemote(inst, "file", "get_cwd"
        ).addCallback(gotResult)

def gotResult(resp):
    print "Got response", resp
    reactor.stop()

def eb(error):
    print "Got error", error
    reactor.stop()

epmd = OneShotPortMapperFactory('client@chloe.ravnaandtines.com', "cookie")
epmd.connectToNode("servernode@chloe.ravnaandtines.com").addCallback(gotConnection).addErrback(eb)
reactor.run()

which involves rather more baggage than using IronPython against my tidying of the C# port of jinterface, for just a "hello world!", given that the jinterface-derived "hello world!" actually does more work (passing arguments, testing results):

import clr
clr.AddReference("OtpErlang.dll")
from System import Array

from Tinesware.Otp import *
import Tinesware.Otp.Erlang as Erlang

cNode = Self("clientnode@chloe.ravnaandtines.com", "cookie")
sNode = Peer("servernode@chloe.ravnaandtines.com")
connection = cNode.Connect(sNode)

args = Array[Erlang.IObject]([Erlang.Integer(1), Erlang.Integer(2)])

connection.SendRpc("mathserver", "add", args)
sum = connection.ReceiveRpc()

if  sum.Value.ToInt32() != 3:
  print "Assertion failed, returned = " + sum.Value
else:
  print "OK!"

And, like py_interface, I find that this doesn't "just work" either:

C:\Documents and Settings\Steve\My Documents\code\babel\erlang>python twotphw.py

Got error [Failure instance: Traceback (failure with no frames): <class 'erlang.epmd.NodeNotFound'>: 1]

which will require a similar amount of rolling sleeves up and getting hands dirty to resolve.