Exploring JVM Languages

A drive-by exploration of JVM Languages: Groovy, Kotlin, Scala and Clojure

Like anyone in the field, I like to keep an eye on what’s going on with the technology.  There seems to be a proliferation of languages that run on the JVM and try to improve on one or more of Java’s shortcomings.  I thought it might be fun to write a simple app in Java and then write the same app in multiple JVM languages.  I chose languages based on what I had the impression I see mentioned a lot: Groovy, Kotlin, Scala and Clojure.  Of those four, the only one I had prior experience with is Groovy, so the example had to be simple.  I also wanted it to have more than just a main class since that’s kind of Java’s thing.  With that unflattering introduction, my simple, contrived example is an application that takes a length and width, instantiates a rectangle and then has it calculate it’s area and perimeter.  For each language, I’ll give a little background overview of the language, give my impressions of it and then talk about the example.

The Java Application

I’ll start by showing the Java application that I started out with.  It contains a Rectangle class and a GeometryCalc class that contains the main method.

The Rectangle class has two fields with their getters and setters, two constructors, a toString method, a method for calculating it’s area and a method for calculating it’s perimeter.  The class is 87% boiler plate code.

public class Rectangle {
   private int length;
   private int width;
 
   public Rectangle() {
      length = 0;
      width = 0;
   }
 
   public Rectangle(int length, int width) {
      this.length = length;
      this.width = width;
   }
 
   public int getLength() {
      return length;
   }
 
   public void setLength(int length) {
      this.length = length;
   }
 
   public int getWidth() {
      return width;
   }
 
   public void setWidth(int width) {
      this.width = width;
   }

   public int calculateArea() {
      return length * width;
   }
 
   public int calculatePerimeter() {
      return 2 * (length + width);
   }

   @Override
   public String toString() {
      return "Rectangle [length=" + length + ", width=" + width + "]";
   }
}

The command line application can take two arguments: an integer length and width.  If only one argument is supplied, the application uses it for both length and width.  There are default values if no arguments are passed in.  Because the user could potentially pass in non-integer arguments, there’s a try/catch for NumberFormatException.  Once the length and width are acquired, the application instantiates a rectangle and has it calculate it’s area and perimeter.

public class GeometryCalc {

public static void main(String[] args) {
   //Default values if no arguments supplied
   int length = 42;
   int width = 20;
 
   if (args.length > 0) {
      try {
         //If only one argument supplied - it's a square use it for length and width
         if (args.length == 1) {
            length = Integer.parseInt(args[0]);
            width = Integer.parseInt(args[0]);
         } else {
            length = Integer.parseInt(args[0]);
            width = Integer.parseInt(args[1]);
         }
      } catch (NumberFormatException nfe) {
         System.err.println("Usage: java GeometryCalc [length] [width]");
         System.err.println(" length: a whole number representing the length of a rectangle");
         System.err.println(" width: a whole number representing the width of a rectangle");
         System.exit(1);
      }
   }
 
   Rectangle rectangle = new Rectangle(length, width);
   System.out.println("Created rectangle: " + rectangle.toString());
   System.out.println("Area: " + rectangle.calculateArea());
   System.out.println("Perimeter: " + rectangle.calculatePerimeter());
   System.exit(0);
 }

Running it with arguments 8 and 4, produces the following output.

Created rectangle: Rectangle [length=8, width=4]
Area: 32
Perimeter: 24

Apache Groovy

http://groovy-lang.org/

Of the languages in this post, Groovy is the most Java-like.  In an effort to require less repetitive code, getters and setters are unnecessary and a lot of standard imports are automatically included. Groovy sells itself as being easy to learn and integrating well with Java while having powerful features such as closures, type inference and static compilation.  Some downsides are that the optional typing can be dangerous and it can be slower in dynamic mode.  Grails and Gradle are well-known members of it’s ecosystem.

From my perspective, it is comfortably Java-like without requiring a bunch of boiler-plate code.  I like that it doesn’t complain when I end my lines with a semi-colon out of habit.  In the area of succinct code, I really appreciate the GString.  Groovy has a number of interesting and potentially powerful ways to represent strings.

Let’s move on to the code example.  To anyone familiar with Java, this should be pretty easy to look at.  The Rectangle has a length and width, a constructor that takes those values, a toString method and it’s methods for calculating area and perimeter.  The toString has a nice example of a GString.  Instead of having to do a bunch of concatenation, we just access that length and width within the quoted string using the dollar sign operator.  And of course, no semicolons.

class Rectangle {
   int length
   int width
 
   Rectangle(length,width) {
      this.length = length
      this.width = width
   }
 
   def int calculateArea() {
      return length * width
   }
 
   def int calculatePerimeter() {
      return 2 * (length + width)
   }

   @Override
   public String toString() {
      return "Rectangle [length=$length, width=$width]"
   }
}

Like the Java application, there is a class that encompasses a main method for running the application.  This also should look quite familiar in how it processes the command line arguments and creates the Rectangle.

class GeometryCalc {

   static main(args) {
      int length = 42
      int width = 20
 
      if (args.size() > 0) {
         try {
            if (args.size() == 1) {
               length = args[0].toInteger()
               width = args[0].toInteger()
            } else {
               length = args[0].toInteger()
               width = args[1].toInteger()
            }
         } catch (e) {
            println("Usage: java GeometryCalc [length] [width]")
            println(" length: a whole number representing the length of a rectangle")
            println(" width: a whole number representing the width of a rectangle")
            System.exit(1)
         }
       }
 
       Rectangle rectangle = new Rectangle(length, width)
       println("Created rectangle: $rectangle")
       println("Area: " + rectangle.calculateArea())
       println("Perimeter: " + rectangle.calculatePerimeter())
    }

}

If we run the Groovy version with the same parameters (8 and 4), this is the output we get.

Created rectangle: Rectangle [length=8, width=4]
Area: 32
Perimeter: 24

JetBrains Kotlin

https://kotlinlang.org/

The big things Kotlin attempts to do is keep developers safe from common errors.  To avoid NullPointerExceptions, Kotlin variables have default values unless you use the nullable form.  It’s a compilation error if you attempt to set a normally declared variable as null or if you operate on a nullable variable without doing null safety checks.  In addition to the null-safety, Kotlin will also automatically cast variables if you’ve done a type check on them.  Google has declared Kotlin to be Android’s official language, so that’s a significant endorsement if you’re an Android developer. Some complaints I saw about the language were about code readability and compilation speed.  There’s a tool for trying out Kotlin from within your browser.

I hadn’t realized that NullPointerExceptions warranted their own language solution.  I’ve certainly encountered plenty of them, but I thought that after one was burned by them enough, null checking becomes second nature.  The declaring return types and types of variables after the method/variable felt awkward at first, but it’s also straightforward enough to get used to.  Personally, I don’t think the syntax would be a difficult adjustment.

The Rectangle class is defined as a data class.  Kotlin provides the data class for cases where the main purpose is to hold data.  For these classes, it automatically derives methods such as toString, equals and hashCode.  It also creates a constructor for the fields defined in it.  So in this example, there’s a derived constructor that takes the length and width.  I’ve created a secondary constructor for initializing an empty rectangle: constructor() : this(0,0).   This is syntactically a significant divergence from Java and Groovy in that the fields are defined right in the first line with the class name: data class Rectangle(val length: Int, val width: Int).  This also demonstrates the type appearing after the variable name e.g. : Int.

data class Rectangle(val length: Int, val width: Int) { 
   constructor() : this(0,0) 
   
   fun calculateArea(): Int { 
      return length * width; 
   } 
   
   fun calculatePerimeter(): Int { 
      return 2 * (length + width); 
   }
}

The first thing you might notice about the main method is… it’s not in a class.  For Java and Groovy, we had a class that only contained the main entry point.  Also different is that in order to exit with an error if the user supplies non-integer parameters, I had to import the exitProcess.  Otherwise, this probably doesn’t look that different.  The if/else, try/catch and println are all relatively familiar.

kotlin.system.exitProcess

fun main(args : Array<String>) { 
   var length:Int = 42 
   var width:Int = 20 

   if (args.size > 0) { 
      try { 
         if (args.size == 1) { 
            length = args.get(0).toInt() 
            width = args.get(0).toInt() 
         } else { 
            length = args.get(0).toInt() 
            width = args.get(1).toInt() 
         } 
      } catch (e: NumberFormatException) { 
         println("Usage: java -jar GeometryCalc.jar [length] [width]") 
         println("  length: a whole number representing the length of a rectangle")        
         println("  width: a whole number representing the width of a rectangle") 
         exitProcess(1) 
      } 
    } 
   
    var rectangle = Rectangle(length, width) 
    println("Created rectangle: " + rectangle.toString())   
    println("Area: " + rectangle.calculateArea())   
    println("Perimeter: " + rectangle.calculatePerimeter()) 
    exitProcess(0)
}

When we run this with our 8 and 4 parameters, here’s our output.

Created rectangle: Rectangle(length=8, width=4)
Area: 32
Perimeter: 24

Scala

https://www.scala-lang.org/

Scala beefs up inheritance with something it calls traits.  Traits appear to be a cross between interfaces and classes.  To improve concurrency, Scala uses futures and promises for asynchronous processing.  Improved pattern matching and the treatment of functions as first-class objects are other points of pride for the language.  Criticisms include steep learning curve, lack of readability and perhaps supporting too many features. Like Kotlin, there’s a tool for trying Scala in your browser.

My simple example didn’t provide an opportunity to delve into the somewhat advanced aspects of programming that Scala claims to improve upon, so I can’t really claim to have formed any kind of educated opinion.   The Rectangle class is the shortest of the examples, but the main entry point is just as verbose as the others.  This could simply be a case of me not making use of a lot of the language’s features.

On that note, here is the very short Rectangle class.  Like Kotlin, the type is after the variable name and the fields are defined right after the class name.  A major syntactical departure is the lack of “return” keyword in the methods.  I was a little chagrined to find that I had to override the toString method to get meaningful output.

class Rectangle(length: Int, width: Int) {
   def calculateArea(): Int = length * width
   def calculatePerimeter(): Int = 2 * (length + width)
   override def toString: String = s"Rectangle [length=$length, width= $width]"
}

The main method is defined in an object rather than a class.  Scala objects behave like Singletons.  The main method looks pretty familiar.  Although the example only catches the NumberFormatException, Scala allows for catching a number of Exception types in one catch clause using it’s pattern matching.

object GeometryCalc {
   def main(args: Array[String]) {
      var length: Int = 42
      var width: Int = 20
 
      if(args.length > 0) {
         try {
            val intArgs = args.map(_.toInt)
            if (args.length == 1) {
               length = intArgs(0)
               width = intArgs(0)
            } else {
               length = intArgs(0)
               width = intArgs(1)
            }
         } catch {
            case ex: NumberFormatException => {
               println("Usage: scala GeometryCalc [length] [width]")
               println(" length: a whole number representing the length of a rectangle")
               println(" width: a whole number representing the width of a rectangle")
               System.exit(1)
            }
         }
       }
 
       val rectangle = new Rectangle(length, width)
       println("Created rectangle: $rectangle")
       println("Area: " + rectangle.calculateArea())
       println("Perimeter: " + rectangle.calculatePerimeter())
   }
}

Running the example with parameters of 8 and 4 gives us this now familiar output.

Created rectangle: Rectangle [length=8, width= 4]
Area: 32
Perimeter: 24

Clojure

https://clojure.org/

Clojure is a dialect of LISP and has the most unfamiliar syntax of the languages over-viewed here.  Rick Hickey was kind enough to dedicate an entire page of the Clojure website to it’s rationalization.  To paraphrase, he wanted a functional LISP that ran on the JVM and was good for concurrency.  The most common complaint I saw about Clojure is it’s cryptic error messages.  Clojure can also be tried out in your browser.  In an interesting departure from other languages, a single line comment is indicated using a semicolon.  e.g. ;this is a comment.

It was a real battle to get anything going in Clojure.  My background is almost exclusively object-oriented, so no doubt that was part of the problem.   Once I got it going though… it was so much good nerdy fun!  It was a struggle, but it was the enjoyable kind of struggle that makes you remember why you went into computers in the first place.

To get a Rectangle class, I used defrecord.  It’s placed in it’s own namespace.  First I defined a defprotocol called Geo that defines the methods calculateArea and calculatePerimeter.  Then the Rectangle defrecord uses that protocol and uses it’s length and width to do the calculation.  Similar to Polish Notation, the operators precede the values being worked with.

(ns amdegregorio.rectangle)

(defprotocol Geo 
 (calculateArea [this] "The area")
 (calculatePerimeter [this] "The perimeter"))

(defrecord Rectangle [length width] 
 Geo
 (calculateArea [this] (* length width))
 (calculatePerimeter [this] (* 2 (+ length width))))

Here’s the primary namespace where the -main entry method is defined.  To use the Rectangle defrecord from the rectangle namespace, I had to require the namespace and import the defrecord.  The parse-int function is something I borrowed from stacktrace for safely parsing the command line args into integers.  Clojure’s syntax for accessing arrays is very interesting with having to use the nth command and then provide the index after the array.

(ns amdegregorio.geometrycalc
  (:gen-class)
  (:require amdegregorio.rectangle)
  (:import [amdegregorio.rectangle Rectangle]))

;from https://stackoverflow.com/questions/5621279/in-clojure-how-can-i-convert-a-string-to-a-number
(defn parse-int [v] 
  (try 
     (Integer/parseInt (re-find #"^\d+" (.toString v))) 
     (catch NumberFormatException e 0)))

(defn -main
    [& args]
    ; Read the args and set up length and width
    ; if no command line args supplied, use default values
    ; if one command line arg sent - its a square use length for length and width
    (if (nth args 0) 
       (def length (parse-int (nth args 0)))
       (def length 42))
    (if (= (count args) 2)
       (def width (parse-int (nth args 1)))
       (if (= (count args) 1)
          (def width length)
          (def width 20)))
    ;create the rectangle and print it's vitals
    (def rectangle (Rectangle. length width))
    (println rectangle)
    (println(.calculateArea rectangle))
    (println(.calculatePerimeter rectangle))
)

When running the application with the same 8 and 4 arguments we get our expected output.  I am using Eclipse and to run the application, I have to right click the project, select the Leiningen menu, select “Generic Leiningen Command Line” and then enter “lein run 8 4“.

#amdegregorio.rectangle.Rectangle{:length 8, :width 4}
32
24

Conclusion

It was really fun to dip into each of these languages, although I freely admit that my example didn’t allow me to exercise them very well.  I’m not sure I would necessarily choose something other than Java for my next project, however.

What do you think?  Did I miss your favorite JVM language?  Do you want to sell me on one of these?  Did I totally miss the point somewhere? If so, please comment.

Sample code can be found on github: https://github.com/amdegregorio/ExploringJVMLanguages

Advertisements

One thought on “Exploring JVM Languages

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s