Tuesday, December 14, 2010

Groovy Puzzler

Consider the following code snippet:


def A = "A"
def B = "B"

def map = ["${A}":1, (B):2, C:3]

println map
println map[A]
println map[B]
println map["C"]
println map[C]


What will be output?

The first thing to consider is, what will groovy think the map is, given the 3 different ways entry are created. However, printing it will return:


[A:1, B:2, C:3]


Looks like it all ended up being the same thing, no?

Now consider what attempting to print a, b and c will give you:


null
2
3
Exception thrown

groovy.lang.MissingPropertyException: No such property: C for class: ConsoleScript27
at ConsoleScript27.run(ConsoleScript27:10)


Now, let's think about what we've done here. With A, we've added it using the normal syntax for dereferencing an object inside of a string, by surround it with ${} inside the string itself, somewhat similar to JSTL. This is returning the string 'A' correctly, but when attempting to reference it, it's not found, thus returning null.

With B, the correct way to dereference and object was used, by surrounding it with a parens. This works as expected, and the referring object can even be used as a key.

C used the string property approach. That is, groovy treated it as the literal string 'C'. This is why referencing it as a ["C"] worked and [C] caused an exception.

The following groovy page contains all you need to know about map manipulation in groovy: http://groovy.codehaus.org/JN1035-Maps

Next, let's consider some of the very useful operations that groovy adds to normal java collections. In this case, max(). What will the following return?
def someList = ["75", 32, 3.2]

println someList.max {item ->
return null
}
The answer is "75". This is because it's the first item in the list. If the closure passed to the max method returns null, the first item in the collection will be returned.

Now, imagine the following scenario. You mess up how you reference the map, inadvertently causing null to be returned for all calls. You then use this map in a closure for the max method. You don't catch any of this because in your unit test the first element should be the max. Now, imagine the list you're operating on was obtained from a database via GORM, which means the order will vary from call to call, since there is no order guarantee without an orderby clause, which isn't there. So, on the webpage you'll see the value change everytime you call it, but when you debug, the map will look correct. Hours later, you'll discover that you didn't create the Map correctly, even though inspecting it in debug, and printing it out in unit testing will show that it looks correct.

While I don't know Groovy that well yet, I can understand at least some of the mechanisms that would lead to that behavior. However, I wonder two things:
  1. Why does the map print out the way it does? I need to do some more digging into the Groovy Map implementation to figure out what underlying mechanism is causing it, but it seems like there could be some kind of way of determining how it's being stored. Meaning, there's obvious differences between the 3 entries, but you won't see it visually when printing, and you won't be able to determine it easily in a debugger. (Although, I may have missed something important when looking)
  2. Should the closure returning null for every element behave this way? I suppose there isn't much way around it, when you consider a scenario where null is returned in some but not all cases.




Friday, December 10, 2010

Groovy MetaProgramming

While working on a Grails project I started investigating groovy meta-programming. I wanted to be able to add one method per class for every Enum in the containing class. (I'll have an example below)

The official groovy documentation on the topic is here: http://groovy.codehaus.org/ExpandoMetaClass

Most examples show something like the following:

//Add new method call printHello
SomeClass.metaClass.printHello = { print "Hello" }
def instance = new SomeClass()

Which of course when run will print "Hello".

However, I didn't know the method was called at runtime, I just wanted to add one for every entry in an enum. It's not hard, but there isn't an example out there, after a little playing in the groovy console, I figured it out:

enum Status {A,B,C}

class User{
def status
}

Status.each{ statusEnum ->
User.metaClass."is${statusEnum.name()}" = {
return status == statusEnum
}
}

def user = new User()
println user.isA()
user.status = Status.A
println user.isA()
println user.isB()
user.status = Status.B
println user.isB()
While will output:

false
true
false
true

Of course, this isn't exactly complex, and I may find that I don't like how this turns out, but I couldn't find an example of this type of Groovy meta-programming example, so I thought I would share.