If you’re tempted to write a method with a boolean return type, consider whether another design might be more appropriate. One classic illustration of the problem with boolean return values can be seen in the standard .NET framework method int.TryParse().

Traditional use looks something like this:

int value;
if (int.TryParse(input, out value))
{
    // Do something with value
}
else
{
    // Do something without value
}

This piece of code is clunky enough that one feature being considered for C# 7 is all about moving the variable declaration inline:

if (int.TryParse(input, out int value))
{
    // Do something with value
}
else
{
    // Do something without value
}

While this is certainly nicer - and the associated scoping rules mean that it will play nicely with other language features like LINQ - there’s a larger issue that TryParse() really needs to return two different pieces of information: Whether the string input could be parsed as an integer; and if it could, the value of that integer.

A better approach (if we were designing this from scratch today) would be to return an Option<T>. If the supplied string can be successfully parsed as a string, the routine would return a Some<int>; if not, the return would be a None<int>. (For those wanting to search for more about this approach, this is known as the maybe monad).

In C# 6, the declaration of TryParse() and subsequent use might look something like this:

public Option<int> TryParse(string value) { ... }

int.TryParse(input).Match(
    none => { /* Do something without value */ },
    some => { /* Do something with value */ });

Admittedly, this is slightly clunky because of the tight scoping of the lambda expressions.

Fortunately, pattern matching features proposed for C# 7 make this much cleaner:

switch(int.TryParse(input))
{
    case None<int>: 
        /* Do something without value */
        break;

    case Some<int> value:
        /* Do something with value */
        break;
}

The version of int.TryParse() we have shown here returns everything you need in one value. This compares favorably with the older version where the answer to “Is this an int?” is returned directly, and the answer to “Which int is it?” is returned via a side channel.

In this example, our opening question “Are Boolean Return values Evil?” the answer is maybe … the existing design for int.TryParse() has room for improvement, but it’s not actually a bad design.

Next time you are writing something akin to TryParse(), have a think about the API before blindly copying what has gone before.

Prior post in this series:
Null arguments are evil
Next post in this series:
What is it with Booleans?

Comments

blog comments powered by Disqus