This is about a bug (or feature?) in Groovy and also about the mindset for learning new programming languages.
Recently, looking at the below code as Java code, I concluded all of the invocations in the above program are valid. Since Groovy derives from Java, I assumed the same is true in Groovy.
However, when I executed the code snippet with Groovy interpreter, it failed at line 6 with the following output
Bar called from Site foo1 This: class A Bar called from Site foo2 Bar called from Site cow Bar called from Site foo1 This: class B Caught: groovy.lang.MissingMethodException: No signature of method: B.bar() is applicable for argument types: (java.lang.String) values: [Site foo2] Possible solutions: wait(), any(), wait(long), is(java.lang.Object), use([Ljava.lang.Object;), each(groovy.lang.Closure) groovy.lang.MissingMethodException: No signature of method: B.bar() is applicable for argument types: (java.lang.String) values: [Site foo2] Possible solutions: wait(), any(), wait(long), is(java.lang.Object), use([Ljava.lang.Object;), each(groovy.lang.Closure) at A$_foo_closure1.doCall(testClosuresAndClasses.groovy:6) at A$_foo_closure1.doCall(testClosuresAndClasses.groovy) at A.foo(testClosuresAndClasses.groovy:8) at testClosuresAndClasses.run(testClosuresAndClasses.groovy:24)
From the output, it seemed the methods were resolved correctly at all call sites executed as part of executing line 22 and 23. By correctly, I mean they were resolved according to the method resolution strategy used in Java, i.e., A.bar was executed when bar was invoked at line 16 as result of executing cow on an instance of B at call site on line 23. However, it seems the same method resolution strategy was not used at call site on line 6 when closure x was executed as a result of executing foo on an instance of B at call site on line 24.
Upon digging deeper, I learned that closures in Groovy are instances of Closure class and they may not be related to the classes (not objects) in which they are enclosed or the classes that define the methods invoked in them. So, I thought “could this be the reason? and added line 5 to print out the type of the receiver being used in line 6. Evidently, its type is B and yet Groovy failed to resolve the method correctly. What is happening?
Upon searching for this bug/feature, I stumbled on these issues in Groovy’s issue tracker.
Apparently, this is a known issue and groovy community is yet to decide if it is a bug or a feature : ) [See the comment thread on issue GROOVY-3010] So, what?
Well, as much as I’d like it to be, this is not about me getting excited about finding a bug in Groovy and contributing to Groovy :) This is about what I took for granted as correct about Groovy because of my Java experience and how it affected my perception of what I observed in Groovy.
Groovy did start out as a JVM-based scripting language that could seamlessly inter-operate with Java and did borrow features from Java (and other languages). However, this does not imply that Groovy has to support these features as done by Java. As a different language, it has the freedom to offer these features but with different semantics. While this is great for experimentation with new programming language features/semantics, it is also a pain for Java developers trying to use Groovy. However, I think the pain is self-inflicted as it stems from incorrectly trying to view Groovy as another kind of Java.
So, when picking up a new language, we can avoid such pains by having an open mind and discarding assumptions and undue expectations stemming from our knowledge of other languages.
That said, the linguist in me would prefer well-defined semantics for Groovy :)
[Note: This post originally appeared here.]