100 Languages Speedrun: Episode 35: Groovy

JVM is a very powerful platform, but Java language is miserable. A bunch of alternatives emerged, Kotlin, Clojure, Scala, Groovy, and JRuby being the most popular ones.

If current trends continue, then in the next few years Kotlin might displace Java as the main JVM language.

But for this episode let's check a less popular contender, Groovy.

Hello, World!

// Hello world in Groovy
println("Hello, world!")

Guess what it does?

$ groovy hello.groovy
Hello, world!

Notice lack of nasty semicolons, and other boilerplate. Normal Java-style comments with // and /* ... */ are supported.

FizzBuzz

1.upto(100) {
  if (it % 15 == 0) {
    println("FizzBuzz")
  } else if (it % 5 == 0) {
    println("Buzz")
  } else if (it % 3 == 0) {
    println("Fizz")
  } else {
    println(it)
  }
}

There's a bit of Ruby-inspired syntax with 1.upto(100) { ... }. There's a lot of other ways to loop, even Java-style for loops.

it is a default list iteration variable, which is a nice shortcut more languages could use.

Fibonacci

def fib(n) {
  if (n <= 2) {
    1
  } else {
    fib(n - 1) + fib(n - 2)
  }
}

for (i in 1..30) {
  println("fib(${i}) = ${fib(i)}")
}

There's no need to declare types if you don't want to, and there's no need for extra return.

We have string interpolation with "${}". Can you imagine string interpolation used to be highly controversial? Even Python was resisting hard, and only finally saw the light in Python 3.6 in December 2016!

This example uses different style of looping. Ranges do the correct thing and 1..30 means 1 to 30 (not 1 to 29 as in some other languages).

Unicode

Just as all other JVM languages (except notably JRuby), Groovy can't do Unicode correctly:

println("Hello".length())
println("Źółw".length())
println("💩".length())

It outputs completely wrong results:

$ groovy unicode.groovy
5
4
2

JRuby pays high performance price for being correct in such cases.

Data Classes

Let's define a very simple data class Point to represent a point in 2D space. We want the class to have nice constructor, and to understand == and .toString(), but we really don't want to write all that boilerplate.

import groovy.transform.ToString
import groovy.transform.EqualsAndHashCode

@EqualsAndHashCode
@ToString(includeNames=true)
class Point {
  Double x
  Double y

  def length() {
    Math.sqrt(x * x + y * y)
  }
}

a = [1, 2, 3]
b = [1, 2, 3]
c = new Point(x: 30.0, y: 40.0)
d = new Point(x: 30.0, y: 40.0)

println(a == b)
println(c == d)
println(null == d)
println("len of ${c} is ${c.length()}")

Which outputs:

$ groovy equality.groovy
true
true
false
len of Point(x:30.0, y:40.0) is 50.0

This is definitely more verbose than Kotlin's data class, but still saves us on boilerplate.

Let's go over it:

  • @EqualsAndHashCode - it defines == and .hashCode() methods, so we can check if two points are equal
  • @ToString(includeNames=true) - it defines .toString() method, so we can print the point - annotations accept various arguments to customize their behavior, in this case we use includeNames=true
  • import groovy.transform.ToString etc. - it's always annoying to import what feels like it should be just part of the language
  • Double x and Double y - static typing is optional, we can say def x and def y instead to have dynamic typing
  • new Point(x: 30.0, y: 40.0) - constructor with keyword arguments was automatically provided by Groovy
  • null == d - it does not crash like in Java, just returns false

Operator Overloading

In one of oh so many terrible decisions Java made was banning operator overloading, so any code that uses any library-defined containers or mathematical objects looks completely unreadable. It's like creators of Java did everything they could to make programmer life maximally miserable on purpose.

Anyway, in Groovy it all works fine. Every operator corresponds to some special name, like plus method is called when you do +:

import groovy.transform.*

@EqualsAndHashCode
@ToString
class Point {
  Double x
  Double y

  def length() {
    Math.sqrt(x * x + y * y)
  }

  def plus(other) {
    new Point(x: x + other.x, y: y + other.y)
  }
}

a = new Point(x: 10, y: 40)
b = new Point(x: 20, y: -20)

println("${a} + ${b} == ${a+b}")

Collections

Groovy makes it easy to work with collections:

s = "Java is a terrible language"

println(s.split(" ").collect{ w -> w.capitalize() }.join(" "))
println(s.split(" ").collect{ it.capitalize() }.join(" "))
println(s.split(" ")*.capitalize().join(" "))
$ groovy collections.groovy
Java Is A Terrible Language
Java Is A Terrible Language
Java Is A Terrible Language

One interesting shortcut is *. which calls a given method on every element of the collection.

Should you use Groovy?

It sure beats Java. Don't use Java.

A more interesting question is Kotlin vs Groovy. Of the main non-Java languages on the JVM, Clojure, JRuby, and Scala are attempting to do their own thing and definitely not trying to take on Java directly. Groovy and Kotlin both do. They're basically saying "if you even want to write Java, you should use us instead".

Kotlin seems to be a bit closer to Java semantics, with everything typed by default and smaller runtime, instead of optionally typed with a bit bigger runtime of Groovy. But to me it seems like minor differences - either of them provides an overwhelming readability and productivity advantage over the pile of shitty boilerplate that's Java.

Kotlin is definitely winning popularity contest in the JVM world right now, but you'll do fine using either of them. Or Clojure. Or JRuby. I'm a bit less sure about Scala.

Also do I mention that Java is shit often enough?

Code

All code examples for the series will be in this repository.

Code for the Groovy episode is available here.