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 useincludeNames=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
andDouble y
- static typing is optional, we can saydef x
anddef 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 returnsfalse
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.
Top comments (5)
I like these posts on programming languages. Most of them I have never heard of. Do you take suggestions btw? I'd like to see how Progress 4GL compares in this list. Progress (or "OpenEdge" which is its newer name) is a database + 4GL language that has been around since the eighties. It supports procedural style code but OO as well. It is mainly used for business application.
Wikipedia: en.wikipedia.org/wiki/OpenEdge_Adv...
A free and fully functional copy can be downloaded from their site: progress.com/openedge/classroom-ed...
(happy to assist)
I only have a couple episodes planned at a time, so suggestions are welcome.
I've never heard of Progress 4GL. As I'm doing one of these a day, I mostly limit it to languages which I can install on OSX with one brew command, and which have enough documentation that I can google my way out of any problems. There's some languages (like PowerShell and SAS) that's been waiting on the list for long as it's some hassle to run them on a Mac.
Groovy is horrible, so glad that I switched to kotlin.
Even java is more fun to write by comparison.
I'm quite interested in some deeper discussion why many people prefer Kotlin to Groovy. I tried to find any posts about it, and couldn't really find any, and in my one day of experimentation, I didn't really find any serious issues with Groovy.
On a high level groovy does not have any issues correct, the languages is actually quite fun to work with.
Just things like
def myInt = 1;
will compile toObject myInt = 1;
. Another thing is that it does not work well with java in the same project, I really had to hack around that.Kotlin on the other hand works amazingly well with java. The better typing that kotlin has is also great.
Groovy also has a lot of weird stuff like
"ls -hal".execute().text
just executing shell commands, this feels very sketchy to me.Fun things that groovy has are no returns (you can probably guess how this becomes confusing if you just have a variable at the end of a function)
although kotlin does it better