Java Getting Closer to Scala’s Expressiveness
Java will never be as expressive as Scala. Scala’s feature set allows for writing complete DSLs as well as concise, terse code. That said, Java is getting closer. Since JDK 14 Java has had preview support for “records” which are similar to Scala’s case classes. In future JDKs records will have near feature parity with Scala’s case classes.
For me, the most expressive feature of Scala is its case classes with pattern matching. Once you’ve coded with this feature you’ll never want to return to the alternatives. This repo shows Java’s progress towards equalling Scala’s case class feature. It’s unlikely that Java will ever completely match Scala but it will get close.
Shown here is a very old interpreter from the Scala examples website. It’s not meant to represent current idiomatic Scala nor is it meant to be a definitive example. However, it’s simple, fits on one page and shows the expressiveness of case classes.
Three Java attempts are shown. Versions in Java 15 and 17 taking advantage of records and Java's initial support for pattern matching. The other in Java 8.
Java 17 Attempt
Java records are now part of the language. My RecordBuilder library is still used for some builder sugar. Huge kudos to Gavin Ray for contributing the Java 17 version using Sealed Classes as well as Pattern Matching for Switch. This is getting very, very close to the Scala version now. Once deconstruction is added we'll have a nearly exact version.
Java 17's enhanced pattern matching for switch is missing reification and deconstruction. So, for now, a hacked
PairNumNum is used to simulate switching and matching
See the Java 15 attempt for what will be permanent issues with lack of a
Pair and type aliasing.
Java 15 Attempt
Java 15’s Records are an interim feature towards full pattern matching and deconstruction. Java’s records are very close to Scala’s case classes (sans pattern matching/deconstruction which will come later). The only missing feature is a static constructor/builder method which makes allocating case classes a bit cleaner (no need for
new). Fortunately, my RecordBuilder library can auto-generate these so I’ve used it here to match Scala.
The major thing missing in this Java 15 attempt is, of course, Scala’s case-match. Future versions of Java will add support for something similar but for now I’ve had to use Java 15’s new pattern matching for instanceof.
Java 15 is also missing a built-in Pair class. With records, however, this is very easy to create. Java also does not support type aliases. It’s unlikely Java will add support for this. Thus, the Scala example’s
Name cannot be duplicated.
These caveats aside, the Java 15 attempt is not too bad. Once we get Java’s version of pattern matching it will be possible to write a near perfect duplication of this Scala example.
Java 8 Attempt
Java 8, of course, does not support anything like records or case classes. However, we can use generators such as Immutables to generate something close to a record. Having to use Java interfaces as a spec for the Immutables builder is not as satisfying and makes the code much more verbose. Also, the Immutables builder does not generate static constructors/builders so these have to be added manually.
Like in the Java 15 example, there is no built-in Pair class and in this Java 8 instance it has to be specified as an interface so that Immutables can generate the implementation.
Lastly, Java 8 does not have Java 15’s pattern matching for instanceof so extra work in the form of casts must be done in the parts of the code that match on operation types.
- Original Scala Example: Scala SimpleInterpreter
- Java 17 Attempt: Java 17 SimpleInterpreter
- Java 15 Attempt: Java 15 SimpleInterpreter
- Java 8 Attempt: Java 8 SimpleInterpreter