Scala vs. a Java Anti-Pattern

In the last few months I've been playing with Scala, a new(ish) programming language that runs on the JVM. The problem with learning a fun new language is that you start wanting to use its features, even when you're still using a different language. (In this case, Java.) But I came across a case this past week that illustrated this so well that I actually wanted to write it down to share.

The Anti-pattern

It's common to need to return more than one item. Maybe a name and date, or an ID and a status, or a result and an error code. Languages like Java don't really make this easy -- the return statement only takes one argument.

This often gets solved by returning some type that approximates a tuple. For example:

// ...
TuplePair<Integer,ErrorType> pair = foo.computeSomeValue();
if (checkErrorConditionOK(pair.getSecond())) return pair.getFirst();
else throw new AppropriatelyNamedExceptionTypeException(pair.getSecond().getErrorMessage());
// ...

This is pretty ugly. But foo has returned a couple values to us. And with Java generics, we even get some compile-time type checking -- pair.getFirst() is an Integer, and pair.getSecond() is an ErrorType. But that's a pretty ideal case with a nice descriptive type parameter. What if our parameters are of the same type?

// ...
TuplePair<Integer,Integer> pair = foo.computeSomeValue();
if (checkErrorConditionOK(pair.getSecond())) return pair.getFirst();
else throw new AppropriatelyNamedExceptionTypeException("Got error code: " + pair.getSecond());
// ...

From this code, I have no way of knowing that the second item in the tuple is an error code. I can infer it from the way the code is written. But maybe whoever wrote this code got the parameters mixed up. If I'm reading through this code (debugging or doing a thorough code review), I now have to open up foo's implementation to see what actually gets returned in that tuple.

In this case, our TuplePair is barely better than an array.

Why the Anti-pattern Persists

It's easy to understand why I keep seeing this pattern in Java, though. There's a lot of boring boilerplate code that has to be written to work around this problem. To make the code more meaningful and readable, and to allow for easier refactoring in the future, you should assign a name to each thing you want to return. (No, you don't want to do that with a Map. That's an anti-pattern for another blog post.) The best way to do this is with a new class.

In Java, writing a new class is pretty heavyweight. If you want to use proper encapsulation, your new class might look like this:

public class ComputedValue
{
 private int value;
 private int errorCode;

 public ComputedValue(int value, int errorCode)
 {
  this.value = value;
  this.errorCode = errorCode;
 }

 public getValue() { return value; }
 public getErrorCode() { return errorCode; }
}

Since ComputedValue is a public class, you have the option of making it a static inner class, or going the more common route and creating an entirely new file. But at least now our code is a bit more readable:

// ...
ComputedValue computed = foo.computeSomeValue();
if (checkErrorConditionOK(computed.getErrorCode())) 
<p>return computed.getValue();</p>else throw new AppropriatelyNamedExceptionTypeException("Got error code: " + computed.getErrorCode())
// ...

What Scala Offers

Given the above option, it’s not surprising that people keep creating the TuplePair class. Frankly, it’s a pain in the ass to write new Java classes for every case like this. The thinking is “Write TuplePair once and re-use it so we don’t have to do it again,” even though it leads to useless names like getFirst() and getSecond().

Scala helps solve this class of problems by making creating these types of classes dead simple:

class ComputedValue(val value: Int, val errorCode: Int)

This single line gives you everything that the above Java implementation did: A type with “value” and “errorCode” properties, with a constructor for easily creating new instances. And while you didn’t have to write any boilerplate code to provide encapsulation, it’s there, implemented automatically by the Scala compiler.

Classes in Scala are public by default, and they don’t have to be declared in separate files or as static inner classes. That makes the barrier to entry to writing better code a single line in a file you already have open. The Scala version of the above snippet becomes:

// ...
val computed = foo.computeSomeValue();
if (checkErrorConditionOK(computed.errorCode)) return computed.value;
else throw new AppropriatelyNamedExceptionTypeException("Got error code: " + computed.errorCode)
// ...

But Scala gives you even one more nice feature in this simple case. So far, we’ve only been looking at code that consumes a ComputedValue instance. But what about the code that creates it?

// Java:
public ComputedValue computeValue()
{
 return new ComputedValue(0, 42);
}

Even with our new ComputedValue class, this code still has the same problem as our TuplePair! Someone reading this code can’t tell which is the value and which is the error code. To be sure, we would have to open up ComputedValue and look at its constructor parameters. But we’re still not sure if the developer knew the proper parameter order when he wrote that line of code. We can’t see his intention from the information in the call to the constructor. I use the following pattern to make this more clear in Java:

public ComputedValue computeValue()
{
 final int value = 0;
 final int errorCode = 42;
 return new ComputedValue(value, errorCode);
}

The above at least makes my intentions clear. But to check whether my argument order matches the argument order expected by the constructor, you’ll have to look at ComputedValue’s implementation. How does Scala improve on this?

def computeValue() = new ComputedValue(value = 0, errorCode = 42)

Again, a single line of Scala replaces several lines of Java. The code here is not only more concise, but more explicit. The return type of computeValue() is inferred by the result of the method’s implementation on the right-hand side. And by using named arguments, we not only make our intention clear to the reader, but also to the compiler. Scala will check at compile-time that ComputedValue’s constructor takes “value” and “errorCode” parameters, and will even re-order our paramters for us if we got them in the wrong order.

These are some of the most basic features that Scala offers, and yet they’re the ones that come up in day-to-day work over and over again. By getting tedious boilerplate out of the way, Scala lets coders write better code without worrying about making more work for themselves.

comments powered by Disqus