Aug 28, 2009

Interfacing between JRuby and Scala

One of the major features of JVM languages is their interoperability. It's really easy to call Java code from a JRuby script, and (while slightly less easy) you can run a Ruby script from within a Java program.

However, how difficult is it to interface between two non-Java JVM languages? It turns out that it isn't really that hard at all! For this post, we'll talk about how to interface between JRuby and Scala, it's really rather simple.

So here's our scenario. We are writing our main app in Scala, because Scala is faster than Ruby. However there are some situations where we don't want to have to recompile the whole thing to make changes, and we want the code to be nice and easy to understand for people who don't know Scala (admittedly, Scala is not a good first language as it is rather complex). We're going to embed some Ruby scripts within our app.

Here's a basic Scala outline in ScalaTest.scala:
import javax.script._   // import Java's scripting API

object ScalaTest extends Application {
var engine = (new ScriptEngineManager).getEngineByName("jruby")

engine.eval(new FileReader("test.rb"))
And test.rb:
puts "Hello from Ruby!"
Before we attempt to run this though, we'll need to get both JRuby, and the JRuby engine. You can grab JRuby from their website (as of this writing the latest version is 1.3.1), just extract that and grab jruby.jar from the lib folder. As for the JRuby Engine, you'll have to grab the big engine tarball from the Java site and extract jruby-engine.jar from the jruby/build folder. Once you have those put them in the same folder as your code, then compile and run:
scalac ScalaTest.scala
scala -cp .:jruby-engine.jar:jruby.jar ScalaTest
What you should see is "Hello from Ruby!" pop up on the screen.

So this is pretty cool, but how do we get information between Scala and Ruby? That part can be a little bit tricky since the type systems in Scala and Ruby are very different. However for our example here, we will stick to simple things. Here is our new Scala code:
import javax.script._

object ScalaTest extends Application {
val engine = (new ScriptEngineManager).getEngineByName("jruby")

val name = Console.readLine("What is your name?\n")

// assign a variable in the engine
engine.put("name", name)

// cast the result of the execution into a string
val colour = engine.eval(new FileReader("test.rb")).asInstanceOf[String]

Console.print(name + ", your favourite colour is ")

// this part is unnecessary, but let's have some fun with colours :)
if (colour.toLowerCase == "blue")
else if (colour.toLowerCase == "red")
else if (colour.toLowerCase == "green")

Console.println(colour + Console.RESET + ".")
And the Ruby code:
# the name variable is passed in as $name, which is a String
puts $name + ", what is your favourite colour?"
While this is a trivial example, we could use this to embed all sorts of functionality within our application. In fact, this isn't even limited to JRuby. By changing the getEngineByName call to say, "Javascript", we can execute Javascript code instead of Ruby code. Or any other JVM language included in that big engine tarball, including Scheme, Jython, and Groovy to name a few. You can even use Java as a scripting language, although you have to jump through a few hoops since Java doesn't support global variables.


ilan b said...

Hey.. fellow Concordia grad here from the class of '93 Engineering! I bet you anything the 8th floor escalator is still on the fritz!

Anyways, I appreciated your article but I wouldn't mention that Scala is difficult for first timers. The fact that Scala has a very clean approach to it's type system and treats functions as first class citizens does not make it difficult. I believe the real issue is established coders getting used to the strong typed syntax. The industry NEEDS people picking up Scala as their FIRST language.. In my day, I went right from assembly to C++ and was the much better for it. Besides your article is addressing interfacing between JRuby and Scala which happens to borrow heavily from Ruby..

Rob Britton said...

These days every escalator is on the fritz. At least you get your exercise.

My reasoning behind not using Scala as a first language is because of the size of it. There is a lot to the language - I'd argue it is even bigger than C++, albeit without little exceptions/pitfalls everywhere - which can be overwhelming to new people. On top of that, scalac can give some really weird errors sometimes, I'm still not entirely certain why they happened and why my "fixes" fixed the issue.
I do agree that the industry could really use people learning Scala as a first language. It gives good exposure to a lot of good concepts which will help people later on.

I'm not really sure what you mean by your last sentence - are you saying that Scala borrows heavily from Ruby?