Groovy vs JRuby – 4:4

Alright. Funny enough. After another day with both, Groovy and JRuby, I would say Groovy did catch up. Here’s the whole story:

After accepting the fact that Groovy is very OO, I found a way to add my plugin’s configuration folder to the classpath and load additional stuff from there. This way I was able to write a simple LexerAdapter and LexerBase class in Groovy. The resulting lexer specification looks something like this:

import jfun.parsec.*
import jfun.parsec.tokens.*
import jfun.parsec.pattern.*

class MyRubySyntax extends jparsec.LexerBase
    {
    protected void setup()
        {
        def s_block_open = Scanners.isPattern( Patterns.regex( /(?m:^=begin)/ ), "BLOCK_OPEN" )
        def s_block_close = Scanners.isPattern( Patterns.regex( /(?m:^=end$)/ ), "BLOCK_CLOSE" )
        def s_block_commented = Scanners.anyChar()

        scanner "DOC_COMMENT",	        Scanners.isBlockComment( s_block_open, s_block_close, s_block_commented )

        regex "LINE_COMMENT",           /(?m:#.*$)/
        regex "SPECIAL_QUOTED_STRING",  /%[qQ]\{(?:(?:\\\})|(?:[^\}]))*\}/
        (..snip..)
        regex "IDENTIFIER",             /\b[a-z]\w*\b/
        regex "NUMBER",                 /\b(?:0x[0-9A-Fa-f]+)|(?:[0-9]+(?:\.[0-9]+)?)\b/
        }
    }

return new MyRubySyntax().lexer()

This is still a lot uglier than the (J)Ruby version. But the thing is: This compiles to Java byte code. And with the LexerAdapter implemented in the scripting part of the system this is significant. To remedy the situation for JRuby I still have the pure Java LexerAdapter available on the classpath. (More on this in the example configuration files.)

What I didn’t figure out: There’s a nice use (“with”) codeword in Groovy. But it didn’t really work. It should provide functionality to merge a module’s methods into a closure/block of code. This way I wouldn’t have to create this MyRubySyntax class, etc and could get away with something like this:

use ( jparsec.LexerBase )
{
    (..snip..)
    regex "NUMBER",                 /\b(?:0x[0-9A-Fa-f]+)|(?:[0-9]+(?:\.[0-9]+)?)\b/

    return lexer()
}

But I guess I still don’t understand Groovy good enough. It has quite a few little quirks.

Another of those would be that I cannot use closures in all the places that should allow them. Here’s one:

for ( idx in 0..myCache.size() - 1 )
{
    def token = myCache.get( idx )
    if ( aStartOffset >= token.start && aStartOffset < token.end ) return token
    if ( aStartOffset < token.end ) return token
}

With myCache being an ArrayList I would expect myCache.each { token -> ... } to work. Somehow I get a compiler error mumbling about 1 parameter where 2 are expected or something like that. Weird.

Anyway, with the compiled approach of Groovy it suddenly feels a lot more practicable for the intended purpose of lexer adaptation and specification. Nice.

With the next step planned - extending the plugin to allow simple parser functionality - I assume JRuby will become even less an option. But it should be helpful to get some quick results. Especially for the Ruby configuration of the plugin. I intend to use one of the existing pure Ruby parsers for the Ruby syntax. We'll see how that turns out..

tfdj

Groovy vs JRuby – 1:4

Admittedly, comparing Groovy and JRuby is like comparing apples and oranges. But my point is: It’s both fruits! :)

Groovy with a compiled approach. JRuby with an interpreter. But nowadays, with the scripting API in Java 6, the really important aspect of both languages is: They can both be used for scripting in a Java context.

While updating my SimpleSyntax plugin to a scripted lexer configuration approach, I started with using Groovy for the scripting. Now, the following ‘issues’ are what I got stuck at. There are probably other – read: correct – ways to solving these problems. But the thing is: Groovy and it’s ‘documentation’ made it hard for me to solve my problems properly. Here’s what I encountered:

  • Every Groovy script is treated as an instance of a class. If you define methods, they are assigned to this ‘script class’.
  • Because of the previous issue, it’s a bad idea to have a script named Something.groovy and define a class Something in it. That is, if you evaluate it as a script. Because Groovy implicitly created a class named Something in which the script executes. (Importing Something.groovy with it being in the class path will probably work. But it’s not what I wanted for my plugin. Well, the Groovy documentation mentions that Groovy is object-oriented and therefore everything is treated as such.)
  • There is no shared ‘global’ method space between calls to GroovyShell#evaluate. To be more precise: Because of the previous issues, you can’t define a method in one script, and call it in another script. Because every script runs in the context of a new class. (I wonder if there is some way around this. Some trick. Something.)
  • Implementing something like ‘source( aScriptFileName)’ for reading a script and evaluating it ‘in place’ is possible, but only with certain limits. Besides the obvious reasons resulting from the previously mentioned issues, there’s a fundamental problem with ‘sourcing’ another script: Because of the compiled approach of Groovy, the right way to include another piece of Groovy code is to use import (I guess). Otherwise you ‘source’ a piece of Groovy code but can’t rely on it’s – for example – contained class definitions to be available at compile time of the ‘calling’ script.

After I solved these issues ‘somehow’ (more in the Example.groovy script in the plugin sources) I started adding support for JRuby.

Here’s what I found: No problems. JRuby has Ruby’s top-level context. You can define a method in script 1, then execute script 2 in the same Ruby instance and access the method defined in script 1. You can implement ‘source’ with a simple call to eval on a script.

There’s even a ‘setCurrentDirectory’ call in the JRuby API which makes things really simple for me with my configuration folder somewhere in IntelliJ IDEA’s file structure.

Well, I’m pretty sure that after spending about two or three hours with both systems, I am far from being an expert in either of them :) But for me the important point is: After a few hours with both systems, JRuby delivered what I needed. Groovy didn’t.

However, I’d really love to here from Groovy pros how to solve the issues mentioned above. I’m sure there’s a proper way to do everything. But I guess it will be a different one. Different to the simple way I was able to employ with JRuby. Am I right?

On the other hand: Getting my lexer out of the script into my enclosing Java plugin is a lot easier with Groovy. You simply return the created object. And use it. It’s the object you expect it to be. With JRuby, however, you have to convert it back into the expected target class using a utility method. (Or at least I think this is they way you have to do it.. :)

Overall, I think Groovy shines if you’re still close to the Java way. What makes sense, I guess.. :) You write Groovy classes, put them on the class path. Everything works. I ‘assume’ JRuby will be a lot more problematic when you want to do this. I didn’t see a JRubyClassLoader, for example, that works like Groovy’s. Well, I guess these are obvious observarions..

If you’re interested have a look at these four files. They are central to the scripting support in my plugin:

tfdj

Closures for Java

I just read through this article on Artima.

It really comes down to keeping Java simple for the stupid masses, doesn’t it? There’s no intelligent argument against closures. Annonymous classes are more useful? Right.. :) The language is becoming too complex? Let’s instead keep it simple and limited as it is so we all can understand it with our tiny brains.. Right.. :)

I just hope closures make it into Java 7. Hell, why not into Java 6.1?

Anyway, there’s a more serious side to it: Again, the whole approach is compiler-based only. I’m not at all familiar with language design and virtual machine implementations etc. But I wonder: Wouldn’t it make sense to start upgrading the virtual machine? Adding support for advanced language constructs on the VM level?

Well, who gives a f*ck? I’ll get back to playing some more CS. Keepin’ it real.. :)

tfdj