Platinum Partner
groovy,jvm languages,groovy++

Groovy.compareTo(Groovy++) - Part 1

My previous article A sneak peek into Groovy++ covered what Groovy++ is, what are the pros and cons of using it, where it fits compared to Groovy and Java and some high level differences between them. The purpose of this article is to go a bit deeper and try out a few more basic examples and see how things differ in various areas.

The idea here is not to suggest one or the other, but just to bring out the differences for our information and to see if / how the differences can be taken care of.

So, let's begin with some examples right away.

1.  Stricter compile time checks 

If we take the following piece of code:

/* Leave it commented to run the dynamic Groovy version; 
Uncomment to run Groovy++ version */
//@Typed
package test

def x = { List list ->
list.size()
}

x(1)

Groovy's not-so-strict type checking allows the above code to get compiled successfully. It fails with a runtime exception because we are invoking x(Integer), where only x(List) is defined. It relies on you to cover such things in your thorough unit testing.

With Groovy++, we get a compile time error - "Cannot find method { List -> ...}.doCall(int)", indicating that it couldn't invoke with arguments (int) on the closure {List ->}

The flip side of the compile time checks is that we lose on the duck-typing.

So, if we have:

/* Commented -> dynamic Groovy version; 
Uncommented -> Groovy++ version */
//@Typed
package test

class Foo {
def greet() {println "Foo says hello"}
}

class Bar {
def greet() {println "Bar says hello"}
}

def c = {greeter -> greeter.greet()}

c(new Foo())
c(new Bar())

With Groovy, we get the benefits of duck-typing. Even though, there is no relation between the types Foo and Bar, we still are able to invoke methods that they respond to.

With Groovy++, we instead get a compile time error "Cannot find method Object.greet()" because it tries to go by the static type of the closure parameter.

2.  On-the-fly type modifications through ExpandoMetaClass

In the following example, we try to add a method to the String class using the ExpandoMetaClass feature.

/* Commented -> dynamic Groovy version; Uncommented -> Groovy++ version */
//@Typed
package test

String.metaClass.foo = { -> println "foo called" }
"".foo() /* call my new method */
With Groovy, the code runs fine and makes the dynamically-added method foo() available on the class java.lang.String.

Although Groovy++ does not support this feature in its fully static mode, it offers a "Mixed" mode for scenarios such as these, where it tries to provide the best of the both worlds.

Here is a little preview of Groovy++'s Mixed mode:

@Typed(TypePolicy.MIXED)
package test

String.metaClass.foo = { -> println "foo called" } // supported

"".foo()

class A {}
class B {}

A a = new A()
B b = a // this assignment produces a compile time error

What the above code shows is that, when you want, you can use the Mixed mode to get the best of the both worlds - Groovy's dynamic features as well as static type checking.

3.  Closures - More like Java's inner classes now

In Java, inner classes cannot refer to the non-final members of its outer scope. For example, the following fails to compile in Java:

void foo(){
String data = "";
class Inner {
void innerFoo() {
System.out.println(data);
}
}
}

Groovy treats closures as inner classes, but places no such restrictions on the access to the non-final members. So, the following code runs fine in Groovy:

void foo(){
String data = 'original';
def cl = {data = 'changed'} // access non-final data of its outer scope
cl()
assert data == 'changed'
}

foo()

Groovy++ comes closer to Java here. It only allows read-only access the non-final data members. If you try to make modifications, you get compilation errors. So the following code fails with the message "Cannot modify final field test.Test$foo$1.data"

@Typed
package test

void foo(){
String data = 'original';
def cl = {data = 'changed'}
}

foo()

Here is what Groovy++ project lead Alex Tkachman has to say about it: "Yes, outer variables are final and it is by design - one of main usecases for Groovy++ is concurrent programming and non-final variables are suicide in this situation."

But what if you are not doing concurrent programming with Groovy++, but, say, you are migrating some Groovy code to Groovy++ that modifies the data from the outer scope in its closures?

Is there a solution Groovy++ offers? - Yes, you can explicitly use the technique Groovy uses behind the scenes and wrap the data you want to modify in groovy.lang.Reference object, as shown in the code below:

@Typed
package test

void foo(){
Reference data = ['original']
def cl = {data = 'changed'} // now even Groovy++ supports modification of non-final outer scope data
cl()
assert data == 'changed'
}

foo()

4.  No direct access to private members anymore

Groovy does not restrict access to a class' private members from outside. So, the following code goes through:

/* Commented -> dynamic Groovy version; Uncommented -> Groovy++ version */
//@Typed
package test

class SecretService {
String secret = "secret"
String getSecret() {
"covered-" + secret
}

private launchOperation() {
"launched"
}
}

def ss = new SecretService()

assert ss.@secret == "secret" // can access the private field directly
assert ss.launchOperation() == "launched" // can access the private method

Groovy++ restricts such private members access at compile time. In the same example as above in groovy++, "ss.@secret" results in "Cannot access field test.SecretService.secret", and "ss.launchOperation()" results in "Cannot access method SecretService.launchOperation()"

5.  A few more minor differences

     a)  Script binding variables

/* Commented -> dynamic Groovy version; Uncommented -> Groovy++ version */
//@Typed
package test

foo = "foo"

assert foo == "foo"

The above code runs fine in Groovy - it defines the variable "foo" in the script's binding. In Groovy++, this is not supported. If you want to use this feature in Groovy++, use @Typed(TypePolicy.MIXED).

    b)  Property style access for maps

/* Commented -> dynamic Groovy version; Uncommented -> Groovy++ version */
//@Typed
package test

def map = [key1: "value1"]

assert map.key1 == "value1"

Groovy supports property style access of map data, as in the example above. Groovy++ does not support it, unless you use MIXED mode.

The End - Part 1

The examples above cover a few things I have been able to figure out about the differences between Groovy and Groovy++ till now. I intend the play more with Groovy++ and try to uncover a more differences. I encourage you to do the same and share your findings.

References:

    1) Project Groovy - http://groovy.codehaus.org/

     2) Project Groovy++ - http://groups.google.com/group/groovyplusplus and http://code.google.com/p/groovypptest

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}