Most functions do something. I mean, that’s why they are called ‘functions’ and not ‘nothing’, right? But lately, I’ve come to appreciate the value of functions that don’t do anything at all.
The two most obvious examples of functions that don’t do anything at all are boolean operators with just the right parameters. For instance, the truth value of X AND 1 is the same as the truth value of X, and the truth value of X OR 1 is the same as the truth value of X.
“Why,” I hear you ask, “would anyone need a value that they already have, and why would they spend a cycle or two on it?”
“And,” I reply, “that is a very good question! But fear not, for I am about to reveal the answer!”
Suppose you are writing a function that does something. In this case, it’s an function that makes an SQL query and returns a result set. Let’s say your database is pretty simple for the sake of example. It’s got a table, Author, which has just two fields: authorId (a unique identifier), and name (the name of the author). Book, which has fields bookId (the unique identifier of a book), name (the name of the book), and authorId (which ties a Book record to an Author record). So something pretty obvious to do would be to return a set of all the book names, associated with the names of their authors.
$my_query = "SELECT Book.name, Author.name
FROM Book, Author
WHERE Author.authorId = Book.authorId
";
$result = db_object->query($my_query);
And that’s about as simple as it gets. Another simple task would be to only list the books by a particular author:
$author = some_other_function();
$my_query = "SELECT Book.name, Author.name
FROM Book, Author
WHERE Author.authorId = Book.authorId
AND Author.authorId = '$author'
";
$result = db_object->query($my_query);
Hopefully you’d have a bunch of junk in there to protect against sequel injection attacks, but I’m ignoring that for now. Ok, suppose we want to get all the books written by a list of authors. If some_other_function()returns a list, our query won’t work. Let’s tinker with it some more.
$authors = some_other_function();
$my_query = "SELECT Book.name, Author.name
FROM Book, Author
WHERE Author.authorId = Book.authorId
";
if ($authors) {
//this way, some_other_function() can return either a
//list or a scalar, and we don't have to care
if (!is_array($authors)) {
$authors = array($authors);
}
$my_query .= " AND (";
for ($i = 0; $i < count($authors); $i++) {
if ($i != 0) { $my_query .= " OR "; }
$my_query .= " Author.authorId = '".$authors[$i]."';
}
$my_query .= " )";
}
$result = db_object->query($my_query);
Now, we can handle lists or scalars. Unfortunately, an empty list might end up being a problem, since an empty set of parenthesis doesn’t have a truth value of 0 like you’d hope for (in MySQL, at least, and probably other databases as well). Plus, the loop is a little awkward – having that weird if statement whose block gets passed up on the first run seems to detract from its readability. And, if the list isn’t integer-indexed, the whole thing goes to hell. We can use a foreach loop instead, but then we have $i as a non-loop variable, sort of butting in there and costing a couple lines (and the associated readability) but not doing much. On the other hand, a function that doesn’t do anything can save the day (take that, sub-space!):
$authors = some_other_function();
$my_query = "SELECT Book.name, Author.name
FROM Book, Author
WHERE Author.authorId = Book.authorId
";
if ($authors) {
//this way, some_other_function() can return either a
//list or a scalar, and we don't have to care
if (!is_array($authors)) { $authors = array($authors); }
$my_query .= " AND (0 ";
foreach ($authors as $author) {
$my_query .= " OR Author.authorId = '$author' ";
}
$my_query .= " )";
}
$result = db_object->query($my_query);
The possible error condition disappears without a test (an empty list of authors will return no results), and the function is more readable. All thanks to a no-op.