Over the years, I’ve noticed that some ideas stick around, even if their usefulness is suspect. Dijkstra called out one in his famous paper: “Go To Considered Harmful”. These constructs and patterns stick around for many reasons: laziness, lack of experience, or just because it’s part of the language.
There’s another one that keeps popping up: the Pair class.
In its more generic form, the Tuple class. This class shows up in various libraries for language like Java or C++, but other languages have similar constructs built-in. These languages often call them “multi-value return types”.
Whatever the name, I find that their use cause more problems than they solve. In every case I can think of, you’re best served to avoid the use of Pair, Tuple and similar constructs. Even if your favorite library or language uses them, there are better ways to return the values you want.
Pair Obfuscates
The main problem I have with the Pair class and it’s relatives, is that it obscures the meaning of the code. Suppose I have a class with the following method:
1 | public Pair<Boolean, String> authenticate(String username, String password) { … } |
We can see that authenticate
returns a Boolean
and a String
, but it tells us nothing about what those values represent. If we’re lucky, the developer wrote some documentation that explains what each value is used for. Even if you are fortunate enough to have documentation, we have to mentally translate what each part of the Pair means when we reference the values in the calling method. Contrast this with a version that returns the type AuthenticationResult
, which might look like this:
1 | public class AuthenticationResult { |
By simply looking at the class definition, I can tell that the authenticate method will return an indication of success or failure and a message describing why. In the calling code, which do you find clearer, this?
1 | Pair<Boolean,String> result = authenticate(username, password); |
Or this?
1 | AuthenticationResult result = authenticate(username, password); |
For me, reading something like result.isSuccess()
is much clearer than result.getFirst()
.
Pair Isn’t Extensible
Another problem with this pattern is that it isn’t extensible. If I want to return different or additional data, it’s much harder with the Pair object.
For example, suppose we decide that our authenticate
method should also return the ID of the user that was successfully authenticated. If I used the Pair class, I have to change the return type to Triple or some other Tuple type to support an additional value. Of course, I probably want to make this backward compatible, which means creating a new method, deprecating the old, adapting the old to the new, and on and on.
If I had just taken the time to return a proper object, I could just add an additional field to the AuthenticationResult
class and be done with it. The signature of the method doesn’t have to change, and I have a container to return that is extremely extensible.
What About Multiple Return Values?
What about languages that support this idea directly in the form of multiple return values (Go, PHP, Python, Perl)? It’s part of the language, shouldn’t we use it?
As with the GOTO statement, just because the language supports it, that doesn’t mean you should use it.
Multiple return value constructs are only marginally better than the Pair/Tuple class hierarchy. They still obscure the meaning of what is returned from a method and it’s harder to extend.
It’s a little better on the calling side. There, at least, you can give meaningful names to the individual return values. For example, something like this:
1 | success, message = authenticate(username, password) |
While it’s a bit clearer what each value represents, it’s up to the caller to make that distinction, not the author of the original method.
If you need to return additional data, you can just extend the list of values returned, but you have to make sure you keep the order of the parameters straight. If the method accidentally reverses the order of parameters during a refactoring, all callers are broken. As you add more parameters, the list of variables you assign to on the calling side gets longer and longer and longer, making the code harder to read and maintain.
Stop Being Lazy
Whether your language of choice supports multiple return values or you have a library with Pair and Tuple classes, don’t use them. I’d much rather see return types like Range (start & end), Point (x & y), and Service (host & port) than a generic Pair object with no semantic meaning.
Do the work and create a real object! It really doesn’t take that long. In Java, it should only take a moment to:
- Create new class (you’ve got a keyboard shortcut, right?)
- Create the fields you want to return
- Generate a constructor (Your IDE does this)
- Generate getter methods (Your IDE does this too)
If all this takes you more than 60 seconds, you need a new IDE or you need to learn/set up the keyboard shortcuts.
Pair, Tuple, multiple return values, whatever you call them, the just don’t save any effort in the long run. They may be appealing to get something working and out the door. But it’s a false efficiency. When it comes to long lived code (and most code lives longer than you expect), writing clear, maintainable code is almost never a waste of time.
Question: Do you use Pair or multiple return values? Why or why not?