Code Tip: Booleans

Using Booleans with Functions

Returning a Boolean or a pair or 2-tuple with a value and a Boolean is often a perfectly good choice. Similarly, passing a single Boolean argument — to a suitably named function — is often the right approach.

But passing a Boolean to a function that accepts two or more arguments is almost always a mistake!

This article explains why and what to do instead.

This article's examples are shown using Python syntax but the ideas are applicable to most modern languages.

Returning a Boolean

Query functions that return a Boolean should normally have names beginning is or has, e.g., isVisible(), hasSubfolders().

For side-effect functions the name usually reflects their action, e.g., container.delete(key) might return True if key existed in the container and was deleted, or False otherwise.

For functions that return a value that may or may not be valid the choices are usually: (1) return None (or whatever the language's NULL equivalent is), or a valid value; (2) return an Option object which essentially encapsulates a valid value and a Boolean; or (3) return a pair or 2-tuple of value (valid or not) and Boolean indicating the value's validity.

In all these cases, returning a Boolean (or Boolean within an Option, pair or 2-tuple) is perfectly sensible.

Passing a Single Boolean

For functions that accept a single argument, making that argument a Boolean works fine — providing the function's name works well with it. For example, setVisible(True) or setEnabled(False).

Passing Multiple Arguments

For functions that take two or more arguments, none of them should be Booleans! (There's a possible exception shown in the box at the end.)

To illustrate why, imagine trying to guess what the following call does:

drawRectangle(p1, p2, True, False)

Most programmers would reasonably guess that p1 and p2 are points representing two opposite corners. But no one can reliably know what the two Boolean arguments are. And even if there was just one Boolean — could True mean fill; or outline (i.e., the opposite of fill); or shadow; or something else entirely? It isn't possible to see at the call site. And this is why for functions that take two or more arguments using Booleans is a bad idea!

What we should do instead is create an enumeration (even if it has only two members) and use that instead:

drawRectangle(p1, p2, DrawStyle.Fill, ShadowStyle.NoShadow)
drawRectangle(p1, p2, DrawStyle.Outline, ShadowStyle.DrawShadow)

Here, there's no question about what the arguments mean. And as a bonus, because enumerations are used it is possible to extend the range of possibilities without changing the API. For example:

drawRectangle(p1, p2, DrawStyle.FocusOutline, ShadowStyle.DrawDeepShadow)

So, we recommend always using enumerations rather than Booleans for the parameter types of functions that take two or more arguments.

Some languages (for example, Python), allow us to specify named arguments whose names are required at the call site. For example, given this Python function signature:

def drawRectangle(p1, p2, *, fill, shadow): ...

callers must specify the last two arguments by name, e.g.,:

drawRectangle(p1, p2, fill=True, shadow=False)

Of course, this doesn't provide the versatility of using enumerations. So we still recommend always using enumerations rather than Booleans for multi-argument functions, even when using Python.

But, what if you can't change the API — for example, because the function you're calling is in a library that you can't change? In such cases you can still improve call site readability by creating Boolean enumerations or Boolean constants, and passing them rather than raw True or False values.

See also Python Programming Tips.