Optional Typing in Groovy
It is possible to embrace the misconception that Groovy is a “weakly” typed language. In actuality, Groovy is an “optionally” typed language, and that distinction is an important one to grasp when understanding the fundamentals of the language. Groovy’s nearest ancestor, Java, is said to be a “strongly” typed language, whereby the compiler knows all of the types for every variable and can understand and honor contracts at compile time. This means that method calls are able to be determined at compile time, and therefore take the onus of their resolution off of the runtime system.
When writing code in Groovy, developers are given the flexibility to provide a type or not. This can offer some simplicity in implementation and, when leveraged properly, can service your application in a robust and dynamic way. Consider the following application code were the UserController is making a call to the UserHibernateDAO to query for a user on the criteria of a first name. In this example, there is a requirement that if there are more than one User records with the provided first name, then we will display the record’s details and the record’s associated index. If there is only one record, then we can display just the record’s details, without any corresponding index.
This code makes use of polymorphism to provide an inherent discrimination between the two business scenarios of single and multiple user records. In a traditional Java application, we would have to provide a more explicit and verbose manner of handling these two scenarios, since the compiler would demand that it know all of the types before it is able to resolve contracts within the code. In Groovy, developers are not bound by such rigidity, and as such, can make use of the fact that Groovy will resolve the object type at runtime to create a simplified implementation. The next section of code demonstrates this reality in a more simplistic context.
Optional typing can be a powerful utility in your development toolbelt, but if not handled responsibly, it can build complexities and obscurities in your application that may leave your code in an unmanagable state. To get a handle on how you can utilize optional typing in Groovy without getting your codebase into an unmaintainable mess, it is best to embrace the philosophy of “duck typing” in your applications.
Duck typing is a style of typing that relies heavily on the readability of your application code. As the adage goes, “if it walks like a duck and talks like a duck, then it’s probably a duck”. Following this simple principle will give you guidance when considering whether or not it is an appropriate time to employ optional typing. In most cases, it is acceptable to use optional typing with appropriately named variables and language-inherent data structures.
The above code shows the principle of duck typing employed with an instance of the User class. As it were, in this listing, the User object “walks like a user and talks like a user”, and therefore it must be a “user”. This code demonstrates a simple scenario where we can very easily read that the user variable was assigned to an instance of the User class, and therefore it’s not very hard for us to figure out what it is. Keeping your code segments small and isolated, where code can be easily deciphered, is an excellent architecture pattern when making use of optional typing.
When not following the principle of duck typing, even small and isolated code chunks can be made unmaintainable when employing optional typing. The example shown below demonstrates an improper use of optional typing within an application. Even though this code segment is relatively small, and the component pieces are well isolated, it’s entirely unclear what types of objects this code is working against. Given that Groovy allows you to optionally type variable arguments to methods, we could realistically pass any object to the process method and see this code fulfill its execution cycle. In a small application, it may be easy to figure out what type of object should be going through this workflow, but in any application that is beyond trivial, this code fails the test of appropriate use of optional typing.
Making use of optional typing in Groovy can garner the benefits of reduced verbosity and improved readability throughout your application’s code base. It is, however, a concept of the language that must be used responsibly. Following the principle and test of “duck typing” will service you as a guide post when determining if a scenario warrants the use of optional typing or not. It may come to be, in some cases, that in favor of a more-maintainable code-base, you need to sacrifice reduced verbosity in favor of a more clearly understood implementation. In those cases, it is perfectly acceptable to fall-back to a more Java-esque style of code authoring; indeed, not everything benefits from idiomatic Groovy. This is a point that the language developers understood well, and have decreed clearly through the insistence that Groovy enhances Java, it doesn’t replace it.
Nice job – I keep reading your stuff and maybe I will graduate from being a ‘Groovy moonlighter’!
Nice. The old “with great power…” adage. We try to enforce typing in the following area’s of our code base.
All class fields
All method return types, never have def as a return type
All method parameters
Basically all contracts I suppose.
Personally, i feel if the type is not obvious, it needs to be declared no matter how good your test coverage is.
Nice writeup, but personally, I would never ever recommend writing anything like this:
def process(instance)
This is bad in many ways.
1) The name doesn’t describe anything
2) It’s unclear what the method returns, if it does return something
3) It’s unclear what the method takes.
So, the described method can mean the following:
void process(Class instance)
void process(Object instance)
boolean process(instance)
List process(instance)
Object process(instance)
void process(List objects)
Adding types to (public) methods is IMO always recommended. It makes things much more clear to the programmer and the consumer of the method. Trust me, I had the pleasure of maintaining a Grails systems with methods like above, and sometimes, in the middle of night, I still wake up screaming.. 😉
Erik
Just to clarify — that segment of code in the post is demonstrating “what not to do”, a fact that we totally agree upon. Indeed, I’ve had the pleasure of even *writing* some code like that, and quickly realizing how difficult it is to maintain.
Thanks for the comment. 🙂
I may be wrong, but my understanding is that “weak” and “strong” typing usually refers to the ability of a variable changing types at runtime depending on the context.
So, weak typing would be what allows me to write:
a = 5 // a is an integer
a += ‘test’ // and becomes a string here
A strong typed language would raise an error at the second expression. This could be either at compile time (in a statically typed language like Java) or at runtime (in a dynamically typed language like Python).
So, I think “static typing” would be a better term to use when referring to Groovy’s optional typing features because it would be clearer that it’s about the type checking that it is done at compile time.
Great advice on the duck typing, anyway! =)
Cheers,
Elias
This post has raised a lot of chatter about what the appropriate words are for defining the typing systems of Groovy and Java. Groovy can arguably be referred to as a dynamically typed language, similar to Python, since types are (can be) inferred at runtime, as opposed to Java, which can arguably be considered a statically typed language because it determines types are compile type. However, in the context of this conversation I’ve chosen to describe Java as a “strongly” typed language, and Groovy as an “optionally” typed language, so as to minimize the cross-over confusion caused by terms like “dynamic” and “static”; indeed, these words have implications in the languages that go well beyond typing, whereas “weak”, “optional”, and “strong” would generally only be thought of in the context of typing (Maven and HTML aside…).
Thank you for your comment.