Bugs you can write in Groovy, but can’t in Java

Whenever the dynamic language faithful passionately dismiss types, invariably a hip religious war ensues that pits those that feel static typing offers a level of safety (does this make you think “We can dance if we want to“?) and those that feel that types are about as useful as platform shoes (myself, I wear them daily). Nevertheless, without types, you can introduce bugs that wouldn’t otherwise exist. Of course, with proper testing, those bugs do tend to show up quickly– type errors and cast exceptions almost always blow up immediately. In my copasetic career, I’ve been fortunate enough to write applications in Python, Groovy, Jython, and Ruby and haven’t yet been bitten by a bogue bug related to an incorrect type at runtime (that wasn’t otherwise unearthed in a hip test).

Yet, without types, and the related rule that return statements are optional, the runtime engine of your favorite dynamic language does have to make some guesses. And it is in this highly magic process that you can inadvertently create a subtle bug.

For example, check out this highly contrived, yet completely legal example written in Groovy:

def calculateNormLength(str){
 if(str.length() == 0)
  return 0
 println "wasn't 0"

 if(str.length() > 3)
  return 3
}

There is, of course, a logical bug in the code above (what happens when someone passes in “cat” for str? The method will return null), but the key point is that the same corresponding code written in Java doesn’t compile:

public int calculateNormLength(String str){
 if(str.length() == 0)
  return 0;
 System.out.println("wasn't 0");

 if(str.length() > 3)
  return 3;
}

If you try and compile the source file containing this dubious method, you’ll get a nice “This method must return a result of type int” message from the compiler.

As you can see, each trippn’ code example is the same, yet, the Groovy one isn’t caught by a compiler– you must actually find this one. You could spot this nefarious defect via a code review or through a few diligently written tests, but nevertheless, you actually have to be proactive.

It should be noted, however, that if you replace the def with int, you’ll find the bug at runtime with a hip GroovyCastException. Of course, you’re trading flexibility for safety in this case.

The static types/no static types religious war doesn’t show any signs of abating, yet, it seems that in some cases, types (and the associated rules that follow) can save you a small bit of trouble. Can you dig it, man?

Related odds and ends
 

10 Responses to “Bugs you can write in Groovy, but can’t in Java”

  1. on 25 Jun 2007 at 12:20 pm Ricky Clarkson

    I’m struggling to see why you think that returning null in the Groovy code is a bug. I’ve never used Groovy, I’m mainly a Java programmer, but I assumed straight away that the code would return null given “cat”.

    Lisp has been returning NIL for aeons with similar code. Writing code that explicitly returns null, or throws an exception, etc., just makes a partial function (that’s a well-defined term, plug it into wikipedia) harder to read.

    Static typing, ultimately, is about proving that the code will work, even for cases you haven’t actually tried yet. Java doesn’t go very far there, e.g., it allows null references, and it doesn’t have a particularly advanced static type system. Haskell, on the other hand, does go very far, and it’s worth looking at, to realise that ultimately if you use static typing to construct proofs, you end up being quite removed from the actual problem you’re trying to solve, and also find yourself reading mathematics papers with Greek symbols in.

    You can fall off the bottom (a pun for those who know) of Haskell’s static typing - for example, this Lisp code is inexpressible in Haskell - the type system isn’t flexible enough:

    ((lambda (f x) (funcall f f x)) (lambda (f x) (if ( (length str) 3)
    . . . . 3))))

    ..giving a compile error. You could, instead, make it signal a condition (which is like throwing an exception but doesn’t destroy the stack) at runtime, instead of returning NIL, but without actually having to litter the code with the rather irrelevant else clauses.

    Also, dynamic language users do use types, they just don’t use static types (usually). Be careful on your terminology. And, there are no guesses to be made by the runtime engine, the language defines the rules. I’ve not yet seen a language that relies on heuristics for its interpretation.

    I’d like to write in a dynamic language, then when I feel like I want to prove the correctness of some code, add static types to it, with the full power of Haskell’s type system (or something better), until I have. When I find myself writing more test code than production code, I know what I’m really looking for is a proof.

  2. on 25 Jun 2007 at 12:43 pm Ricky Clarkson

    A stray < destroyed some of my last post. A preview button would be good!

    Here’s the missing bit again (clipboard to the rescue):

    ((lambda (f x) (funcall f f x)) (lambda (f x) (if (<= x 1) x (* x (funcall f f (1- x))))) 5)

    (To explain that, the inner lambda is a factorial function that takes itself as a parameter. The outer lambda is a combinator that takes a function (the factorial) and a number, and calls the function, passing it to itself. Here’s the same code written with a name, *fac*:

    (defparameter *fac* (lambda (f x) (if (<= x 1) x (* x (funcall f f (1- x))))))

    (funcall *fac* *fac* 5)
    - outputs 120
    )

    There are apparently other static type systems that can express this (infinite types), but it makes you realise that when you’re using Lisp, you just don’t have to think about the maths there. It’s not even as if the environment is doing the thinking for you - it just isn’t there. You don’t have to be able to prove code to run code.

    So, having dropped off the bottom of static typing, and gone around to Lisp, Ruby, Groovy, etc., you will find yourself making the occasional type error, and missing Java-quality refactoring tools. That’s where optional static typing comes in.

    In this particular case, in Lisp you could write a macro that checks your functions to see whether they definitely return a value, resulting in code like:

    (defun-explicit-return calculate-norm-length (str)
    . (if (zerop (length str))
    . . 0
    . . (progn
    . . . (print “Wasn’t zero”)
    . . . (if (> (length str) 3)
    . . . . 3))))

    ..giving a compile error.

  3. on 25 Jun 2007 at 1:56 pm Andy

    Ricky- You raise a valid point– is it really a bug? And yes, “guess” is a rather misleading term, man. But I think the point is still valid– given the Groovy method as defined, Groovy realizes that there are more than two possible paths, so it needs to handle the 3rd default path and assumes the author meant null, when in reality, the developer “forgot” to handle a 3rd condition (probably thinking about disco dancing rather than the task at hand) and was intending to return another Integer value. Whether or not you define this as a bug, the point being that you can’t write this method in normal Java (for better or for worse).

    Thanks for your input– your thoughts on Haskell are extremely interesting!

  4. on 25 Jun 2007 at 2:39 pm Kevin Kleinfelter

    #define compile execute_unit_test

    Given that many dynamic languages have no explicit compile step, I assert that execute-unit-test takes the place of an explicit compile step.

    I used to be a strong advocate of explicit static typing. Now I’m less certain of the value. I’m certain that, as commonly used, explicit static types are of less value than we might expect. For example, I frequently see variables defined to be integers, when the correct values range over a much smaller range than -MAXINT to +MAXINT.

    It has been suggested by others that compile-time type checking is just an exceptionally weak form of unit test. If I write a function that is expected to return only 0 or 3, I have a responsibility to write a unit test *before* I write the function that confirms that the function returns only a 0 or a 3.

    However, I do think that some sort of static type definition may be useful as a part of automated unit testing. If I specify that a function accepts an integer parameter and returns an integer value, an automated unit test tool that exercises the function by sending it a broad range of integers and validates that it returns only integers, would be a useful tool.

    Using the example from the original posting, a unit test spec that defined allowable inputs as being all strings and allowable outputs as 0 or 3, would quickly demonstrate the bug and would validate correct behavior much more thoroughly than compile-time type checking.

    So I come to the theory that static definition of types is not useful at compile time, where they are commonly used (currently), but they may be useful at unit-test time, where they are infrequently used!

  5. on 25 Jun 2007 at 7:01 pm Andy

    Kevin- great point!! I agree with the unit test - compilation notion; however, given that not everyone actually takes the time to write tests, we’ll have to settle for mandatory technologies like compilers for the time being!

  6. on 25 Jun 2007 at 11:43 pm Ricky Clarkson

    Andy, yes, of course there is a difference between the Groovy code and the Java code. I assert that it won’t matter though. A good programmer will read that code closely enough (at least when it fails) that they will see that it is a partial function. Further, there’s nothing stopping you from interfacing with a Groovy parser to discover partial functions (as I demonstrated, but I used Lisp).

    You can tool yourself to the hilt, qualify everything with static types and write more unit test than production code, but there’s no getting away from the fact that when humans write code, they need to think about it. That’s a great reason to make machines write code. ;)

    Testing or production will reveal the logic error, and if you are using a statically-typed language, then you’ll see the logic error there too (you’d have to make the input parameter of type “a string of length 0 or >3″).

    It’s hard to say that the author forgot (unless you are the author, which, um, you are). They might have known Groovy’s rules, and been merely using them on purpose.

    About mandatory static typing in general - any language where 1/2==0 is a bit dodgy.

    Kevin appears to be looking for Haskell’s QuickCheck (which has been ported to some other languages) but I’m unsure.

    “given that not everyone actually takes the time to write tests, we’ll have to settle for mandatory technologies like compilers for the time being!”

    Perhaps it’s worth not considering everyone. Use what fits you the best. You don’t have to aim for the lowest common denominator.

  7. on 27 Jun 2007 at 7:04 am Sidu

    +1 to Ricky’s comment. If you can’t be sure everyone involved in a project is writing unit tests, then that is sufficient reason to not use Groovy/Ruby/Python/etc. and stick with ol’ Java/C#/C++.

  8. on 30 Jun 2007 at 9:22 pm Stephan Schmidt

    I love expliciet typing. For some time I thought I like type inference like in Haskell, but having expliciet types makes reading some code you are newly responsible for much easier.

  9. on 17 Feb 2008 at 8:24 pm Type Of Cat Litter

    Choosing The Right Cat Litter Box…

    Cats are primarily clean animals, but the cat litter box they use is usually a different story altogether. Pet owners have a wide variety of cat litter boxes to choose from. Scoop litters are a big help for those who want to keep a clean household. If …

  10. on 22 Aug 2008 at 7:40 pm Raoul Duke

    @Kevin
    “For example, I frequently see variables defined to be integers, when the correct values range over a much smaller range than -MAXINT to +MAXINT.”

    thought: perhaps there is a way of instead of throwing the baby + bath water out, having better typing? some pascals let you restrict the range. perhaps dependent typing some day will let us do similar and more powerful things.

Trackback this Post | Feed on comments to this Post

Leave a Reply