Channels ▼
RSS

Open Source

Language of the Month: Groovy++, How Java and Groovy Can Be Better


During the last seven years, we have seen a real renaissance in programming languages on the JVM platform: Groovy, Scala, Clojure, JRuby, Jython, and Mirah to name a few. As a result, for many people today, the real meaning of word Java is not the programming language, as such, but the JVM as a platform: It allows software components written in all these different languages to interoperate as parts of the same application. We programmers have great freedom to choose the right tool for the job.

Of course, each language has its own benefits and downsides. Scala brings a lot of very interesting ideas on combining object-oriented and functional programming together with a much more powerful (but complex) type system, and a very different syntax from Java. Clojure, JRuby/Mirah, and Jython bring a charming taste of Lisp, Ruby, and Python respectively to JVM, which makes it easier for developers from these worlds run on the JVM.

Groovy brings Java developers a familiar, more convenient, and less verbose syntax along with a very powerful library for everyday work. Groovy is our personal favorite, but we might be biased as we are both members of the Groovy core development team.

Unfortunately for Groovy users, because Groovy is a fully dynamically typed language, it runs slowly, does not allow compile-time type checking, and limits the usefulness of static code analysis. This is where Groovy++ changes the game. Let's compare Groovy with Groovy++.

Groovy and Groovy++

Groovy is a popular and successful dynamic scripting language for the JVM. It has extended the Java platform in many exciting ways and brought powerful language features and frameworks to Java developers while remaining very close to Java at syntax and semantic levels. Groovy's powerful language features combined with its ease of use can soon become an addiction, and once you are addicted to it, going back and writing Java code can be a difficult thing to do.

The Groovy project website is a great source of information and references about the rich ecosystem around Groovy.

Groovy++ is a statically typed extension to the Groovy programming language (it's just one jar file to add to your project). It provides:

  • compile-time checking of code
  • performance equal to compiled Java code (see Table 1)
  • easy mixing of statically and dynamically typed code
  • powerful type inference
  • tail recursion
  • traits (interfaces with default implementation)
  • extension methods (compile time categories)
  • standard library of utilities for functional programming, concurrency and distributed computing

Both Groovy and Groovy++ try to achieve the same goal — letting Java developers be more productive without going too far from the syntax, tools, and technologies they already know. Groovy approaches this goal from a dynamically typed angle and Groovy++ completes the picture by providing the statically typed part of the story with the ability to mix both dynamic and static parts together.

If the Java programming language had evolved faster over the last five years, then it very well might look and feel similar to Groovy and Groovy++ combined.

Type inference and implicit casts

Although Groovy++ needs the type information for its static type checking, it uses local type inference technique and implicit type casts. This is a fundamental part of Groovy++ and acts as a guiding principle for everything else.

Type inference

Type inference reconstructs omitted type annotations (like String or List<String>) and aims at providing the same concise feel as dynamic Groovy. Because the goal of Groovy++ is to provide the static semantics as close as possible to the dynamic semantics, type inference differs from that found in most statically-typed languages.

There are many cases where Groovy++ infers your program's types:

  • variable declaration
  • closure parameters types
  • types of variables used in certain switch/case
  • method call arguments represented by list/map/closure expressions
  • parameters of overridden methods

Groovy++ does not allow the method return types or parameter types to be inferred, since they are considered an important part of the method contract and should be visually apparent.

Implicit casts

At times, the Groovy++ compiler complements type inference by doing the opposite operation: inserting implicit casts into the code when needed. This is done in the following cases:

  • assignment to typed variables/fields/properties
  • return statements (including implicit ones)
  • method arguments represented by list/map/closure expression
  • brunches of ternary expression, which are implicitly or explicitly cast to a type

In all cases, the compiler creates an error if the types are not convertible. Note that the convertibility rules are a bit more relaxed than in Java.

Listing One demonstrates implicit casts and type inference working together. The comments describe what is going on.

Listing One

import java.util.concurrent.Callable
import java.util.concurrent.Executors

@Typed String foo() {
   // typeof(executor) == ExecutorService inferred
   def executor = Executors.newFixedThreadPool(2)
   // typeof(res) == List<String> inferred
   // we first found method matching closure 
   // then use calculated return type of closure 
   // to improve closure type to Callable<List<String>>
   // then we find type of call to method submit 
   // to be Future<List<String>>
   // Finally, method get gives us List<String>
   def res = executor.submit(
     // closure expression transformed to class implementing Callable
     // we need explicit cast here as method submit is 
     // ambiguous otherwise
     (Callable) { // Callable<List<String>> inferred
       def substring = 'y' // typeof(substring) == String inferred

       def results = ['']  // typeof(results) === List<String> inferred
       results << substring.toUpperCase()
       assert results[1].toLowerCase() == substring

       results << 1 // implicit cast to String inserted
       assert results[2] instanceof String

       for(el in ['yellow', 'red', 'blue']) { 
    // typeof(el) == String inferred
           if (el.toUpperCase ().contains(substring.toUpperCase())) {
              results << (el != 'yellow' ? el : 239) 
 // note: implicit cast of int to String inserted
           }
       }
       assert results[3] == '239'

// implicit return here, which effects typeof closure 
 // which becomes Callable<List<String>>
       results
     }
   ).get()

   res.each{ // typeof(it) == String inferred
      println it.toUpperCase ()
   }
   
   // implicit return and implicit cast to String here because 
   // of method return type res
}
assert foo() == '[, Y, 1, 239]'

The @Typed annotation is a Groovy++ statement that tells the compiler that the whole script should be compiled statically.

Additional Productivity Features

There is more to improving Java than just type inference and implicit casts. Groovy++ strives to make development enjoyable and pleasant by adding more visible features to the language, extending not only Java, but Groovy as well.

List/map/closure expressions

There are three special types of expressions in Groovy++ that make the language more expressive to both Java and Groovy programmers:

list expression - [a, b, c]
map expression - [k1:v1, ...]
closure expression - { -> ... }


Each of these expressions has the same meaning in Groovy and Groovy++: They create an ArrayList, LinkedHashMap, or groovy.lang.Closure, respectively. But in the situation of a type cast (including implicit casts) the meaning of the respective expressions are different and represent new instances of the required type. Any such transformations can lead to transformations of nested elements as well.

Let's explain this with a few examples without going into a lot of detail. Given this code,

[a, b, c] as T[] means: new T[]{(T)a, (T)b, (T)c}


the compiler verifies and checks each element of the list to tries to make sure that the elements are of the correct type. This is one example of how static typing is being brought into the dynamic Groovy language.

[a, b, c] as LinkedList<X> 

means something similar to:

new LinkedList<X>().addAll((X)a, (X)b, (X)c) 

Again, each element of the list is type-checked. This happens at compile time, not at run time, which catches many type errors earlier.

(Type)[a, b, c] means new Type(a, b, c) 

Lists can be implicitly cast into constructor calls. This option opens interesting opportunities in combination with implicit casts. For example, CountDownLatch cdl = [1] is equivalent to CountDownLatch cdl = new CountDownLatch(1).

Here is an example using map expression

def map = (SortedMap)[a:10, b:12, toString: { "I don't support it" } ] means

TreeMap map = new TreeMap () {
    {
map.put(‘a', 10)
map.put(‘b',12)
    }
    
    public String toString () {
       return "I don't support it" 
    }
}

Here is one more example showing single method interfaces. Too see real gain compare to Java, we also translate this sample to Java. We believe difference speaks for itself.

// Groovy++ version
def executor = Executors.newFixedThreadPool(2)
Executor delegatingExecutor = { executor.execute (it) }
delegatingExecutor.execute { println "done" }

// Java version
final ExecutorService executor = Executors.newFixedThreadPool(2);
Executor delegatingExecutor = new Executor() {
    public void execute(Runnable it) {
        executor.execute(it);
    }
};
delegatingExecutor.execute(new Runnable(){
    public void run () {
         System.out.println("done")
    }
});


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video