Updated: Jun 14, 2018
Full disclosure: This is a teaser for a full blown talk. There are plenty of other examples, do let me know if you’re interested!
Just one example
Let’s pick a canonical example: logging. A Java class emitting logs usually looks something like this (gist):
This is obviously an artificial use case, but not dissimilar from what Java developers deal with a hundred times every week. While trivial, this code already exhibits two fundamental problems:
Boilerplate: Having to import your logging framework of choice (be it log4j, slf4j or java.util.logging) and instantiating a logger is just a hassle, reduces readability of the code and adds zero intrinsic value.
Eager evaluation: All logged messages are generated ahead-of-time, whether or not they’re used in practice.
Boilerplate is annoying, but can largely be ignored. Indeed, Java developers have spent the last 20 years training themselves to filter out large swaths of text, as well as automating the tedious task of actually writing all of that code. Eager evaluation, on the other hand, is a real problem traditionally solved with if statements, or more recently formatting strings:
Don’t ever do that. Other than the runtime overhead, this has the ironic side-effect that, because mistakes are easy to make, it’s liable to crash your application as soon as you enable debug logging, i.e. when you need it the most. Static analysis tools help somewhat, but let’s face it: no-one tests their logging code!
Surely we can do better?
Let’s open the Java 8 toolbox and see what we can do to improve matters, starting with the boilerplate. One way to tackle this is to redefine what we’re trying to do: we want to package a bit of reusable functionality, so that any class author is able to declare that their class emits logs, and get all of the conveniences for free.
The accepted object-oriented terminology for this is a mixin, but Java has no out-of-the-box support for those. Java 8 provides a seemingly analogous feature in default methods; in effect, we can declare a partially-implemented interface:
Default methods can be considered a limited form of mixins, and are used to great effect in the new streams API. With the above interface, we can elide a lot of the undesirable boilerplate:
While this looks nice and clean, there’s a fundamental issue here: the logging infrastructure (and even the logger itself) are visible to consumers of the class! Java mandates public visibility on all interface methods, default or otherwise. While there are ways around this (composition, static import, etc.) they all end up reintroducing much of the undesirable boilerplate, and may have performance ramifications. In effect, there’s no way to reduce this boilerplate.
What about lazy evaluation, you ask?
This is one case in which Java 8 actually helps. A lazily-evaluated string is, fundamentally, a parameterless function that returns a string; with lambdas, we can reuse the Supplier<T> functional interface from the Java class library:
This actually ends up nice and clean at the call site:
While there’s no easy way to get rid of the empty parens in Java, this is actually fairly close to an ideal solution.
What about Scala?
Even if you haven’t delved into Scala at any depth, at this point the following code excerpt should not surprise you:
Cosmetic differences aside, there are really only two Scala-specific concepts here:
Logging is a trait. Scala traits are somewhat analogous to partially-implemented Java interfaces, with several crucial distinctions: you can extend multiple traits (in effect giving you a form of multiple inheritance), traits can have state, and traits support full visibility modifiers. Put together, this means traits can be used to inherit, though not expose, behavior, effectively making them full-blown mixins!
Notice that little lambda-looking arrow before the parameter type? A parameter annotated this way is called a by-name parameter and is always lazily-evaluated.
So what does the user code actually end up looking like?
Note how similar this is to our idealized class. We get nice, boilerplate-free code without having to expose the underlying logging functionality to our own consumers, and we get zero-effort lazy evaluation without changing the look or semantics of the code (in fact, it looks like a regular logging statement). Win!
I hope this post gives you some idea of the power Scala offers, and that you’ve noticed the complete absence (thus far) of common Scala-related buzzwords like functional programming. The point here is that Scala, even at it’s simplest and most familiar form, offers tremendous advantages over Java 8 for any practicing developer. In many cases, and often quite mundane ones, Scala’s expressiveness and conciseness can help you do your job better, safer, faster.
You can do it. You have the technology.
This post was written by Tomer Gabel
You can follow him on Twitter