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.




9 comments:

Danno Ferrin (aka shemnon) said...

regarding the maps:

The first issue is that the string "${A}" will result in a groovy.lang.GString, not a String.

The second issue has to do with the unbound reference to C. Since it looks like a variable it will be treated as a variable. And since it is not declared it is an 'unbound variable' that will be looked up in the binding. The script could work if called from some other source and passed in with a binding where C is defined to some value.

Lucas Ward said...

@Danno

Thanks for the clarification. I assumed it was probably something related to GString. It reminded me of a classloader issue where I thought I had the same two classes, but it turns out they were loaded from different classloaders. I've seen quite a few bugs related to confusion around String and GString when googling for answers. I'll have to make sure I do more research on it.

I understand the issue underlying your second point. When within [] tags it assumes it's a string, kind of like a ruby hash, but outside of that it considers it to be a variable, which it isn't finding. It can still be an easy mistake to make, since only position relative to brackets indicate usage. It does throw an exception when hit though, so its something I would likely catch in a unit test. It still can be confusing if you're not used to looking at Groovy code.

Is there some easy way of seeing the difference between a GString and a String at runtime? Or is it just familiarity with Groovy over time?

Wombatolog said...

I try to println the element of this map by first key (GString = "A"), result is null again.

groovy:000> println map[map.keySet().iterator().next()]
null
===> null

Seems like a bug in the groovy map.

Denisa Cretu said...

Thanks for taking the time to discuss this, I feel about it and love learning more on this topic. If possible, as you gain expertise, would you mind updating your blog with more information? It is extremely helpful for me.
kizidaily.com , juegosfrivas.com , friv2gamers.com

eric yao said...

Coach Outlet http://www.coachstoreoutlet.us.org

Christian Louboutin Shoes http://www.christianlouboutinshoe.eu.com

Valentino Shoes http://www.valentinoshoes.eu.com

Michael Kors Outlet http://www.michaelkorsoutlet.us.org

Coach Factory Outlet http://www.coachfactoryoutlet.us.org

Coach Outlet Online http://www.coachoutletonlinestore.us.org

Coach Purses http://www.coachpurse.us.com

Kate Spade Outlet http://www.katespadefactoryoutlet.us.com

Toms Shoes http://www.toms-shoes.name

Hermes Belts http://www.hermesbelts.us.org

Louis Vuitton http://www.louisvuitton-outletstores.us.com

Fendi Handbags http://www.fendibelt.us.com

Giuseppe Shoes http://www.giuseppezanotti.eu.com

Michael Kors Outlet http://www.michael-kors-outlet.us.com

Stephen Curry Shoes http://www.stephencurryshoes.us.com

Salomon Shoes http://www.salomonshoes.eu.com

North Face Outlet http://www.northfacesoutlet.in.net

Coach Outlet http://www.outletcoach.in.net

North Face Outlet http://www.northfacesoutlet.us.com

Burberry Outlet http://www.burberryoutletstore.us.org

North Face Outlet http://www.north-faceoutlets.in.net

North Face Jackets http://www.outletnorthface.in.net

Skechers Shoes http://www.skechersshoes.us.com

Toms Outlethttp://www.tomsoutletstore.us.org

North Face Outlet http://www.northfacestoreoutlet.us.org

Nike Air Max http://www.nikeoutlet.us.org

Nike Hoodies http://www.nikehoodies.us.com

Marc Jacobs Handbags http://www.marcjacobshandbags.name

Marc Jacobs Outlet http://www.marcjacobsoutletstore.us.com

Jimmy Choo Shoes http://www.jimmy-chooshoes.us.com

Jimmy Choos http://www.jimmychoos.in.net

Burberry Belt http://www.burberrybelt.us.com

Louis Vuitton Belt http://www.louisvuitton-belt.com

Salvatore Ferragamo http://www.ferragamo-belt.us.com

Marc Jacobs Handbags http://marcjacobshandbagsoutlet.blog.com

Lululemon Outlet http://www.lululemonsoutlet.us.com

True Religion Outlet http://www.truereligion-outlets.in.net

Tommy Hilfiger http://www.tommyhilfigeroutlet.us.com

Michael Kors Outlet http://www.outletmichaelkors.eu.com

Coach Outlet http://www.outletcoach.eu.com

Red Bottoms http://www.redbottom.in.net

Kevin Durant Shoes http://www.kevindurantshoes.us.org

New Balance Outlet http://www.newbalanceoutlet.in.net

Adidas Outlet http://www.adidasoutlet.in.net

Coach Outlet Online http://www.coachoutlet-online.eu.com

Stephen Curry Jersey http://www.stephencurryjersey.us.com

Vans Outlet http://www.vansoutlet.us.com

Ralph Lauren Outlet http://www.ralphlauren-outletstore.in.net

True Religion Outlet http://www.true-religionoutlets.in.net

ED Hardy Outlet http://www.edhardyoutlet.in.net

North Face Outlet http://www.northfaceoutlet.eu.com

UGG Outlet http://www.uggoutlet.us.org

UGG Outlet http://www.outletugg.in.net

North Face Outlet http://www.northface--outlet.us.com

Ugg Boots Sale http://www.uggbootssale.in.net

UGGS For Women http://www.uggsforwomen.us.com

Skechers Go Walk http://www.skechersgowalk.us.com

Adidas Yeezy Boost http://www.adidasyeezyboost.in.net

Adidas Yeezy http://www.adidasyeezy.us.com

Adidas NMD http://www.adidas-nmd.us.org

Coach Outlet http://www.coachoutletinc.us.com

for IT the said...

I have read your blog its very attractive and impressive. I like it your blog.

Java Training in Chennai Core Java Training in Chennai Core Java Training in Chennai

Java Online Training Java Online Training Core Java 8 Training in Chennai Core java 8 online training JavaEE Training in Chennai Java EE Training in Chennai

for IT the said...

Hibernate Online Training Hibernate Online Training Hibernate Training in Chennai Hibernate Training in Chennai Java Online Training Java Online Training

Hibernate Training Institutes in ChennaiHibernate Training Institutes in Chennai Java Online Training Java Online Training Java Online Training Java Online Training

for IT the said...

Java Training Institutes Java Training Institutes JMS Training Institutes in Chennai JMS Training Institutes in Chennai | JSP Training Institutes in Chennai | MicroServices Training Institutes In Chennai Java MicroServices Training Institutes In Chennai

Tina Shinee said...

Thank you! Do you like this. Top best free online flash 3D.
Kizi 100 | Yoob Friv | Y8