Language Usability: Impied Convenience
Red-tape languages like Java and C, which require explicit casting and type conversion haven’t gained traction on the web precisely because of their type-system bureaucracies. When HTTP request parameters are all strings, having to explicitly convert them into some useful datatype is another hurdle to be cleared by programmers.
Implicit type conversions are amazingly helpful for rapid development, but they often come at a cost, clouding the meaning of a program and making it difficult to anticipate the result of an operation. PHP’s type conversion system, for example, introduces special cases when making comparisons between different datatypes. These special cases make the overall behavior of comparison operators unpredictable. For example, I expect equality to be transitive: if A equals B and B equals C, then A will equal C. It’s one of the basic properties of equivalence, so it’s not unreasonable to expect it of the equality operator in PHP.
Equality isn’t transitive in PHP, and it’s easy to use that blot to brush off implicit type conversion as a darling of lazy programmers1, but I hope to illustrate that it’s the implementation that’s broken, not the feature.
An Admittedly Contrived Illustration
PHP tries to convert values to similar types before comparison. In the case of comparing a string with a number, the string is parsed and its numeric value is used in the comparison. If the string doesn’t start with numeric characters, the value 0 is used instead. Therefore, the following is true.
php> "Nothing" == 0; // Does A equal B? Yes.
Okay, it’s weird to have a special case here, but there are times when automatically converting strings to numeric values can be useful, so we’ll let it slide for now.
What about the NULL keyword? That’s just a fancy name for 0, right? They certainly appear to be equivalent:
php> 0 == NULL; // Does B equal C? Yes.
Even though it appears straightforward, we’re actually dealing with another special case. When PHP compares the special NULL value with any type, both are converted to booleans before comparison. The literal integer 0 and the special type NULL are both considered false in the above comparison, and in a roundabout way, the values are equivalent.
Here’s where things get weird. If we want the equality comparison operator to be consistent with its usual mathematical properties, then the string "Nothing" will have to be equal to NULL. “No fair,” I hear you say, “you’ve set us up for a paradox!”
Come on, it’s not like you didn’t see it coming. Here’s what happens:
php> "Nothing" == NULL; // Does A equal C? No.
Oh, snap! We just broke transitivity. Sure enough, in PHP we have yet another special case, in which NULL is converted to the empty string before a comparison with a string object. Since "Nothing" does not equal "", the above evaluates to false.
Never has something so confounding been invented in the name of simplicity. While the language designers had good intentions—after all, everyone wants to be able to easily compare values of different types—the programmer is given a rat’s nest of conversion rules.
Implementation is Everything
We have just seen implicit type conversion do nasty, terrible things to usability. When programming, the sooner you know something may not do what you expect, the better. But if using an allegedly time-saving implicit conversion requires you to stop and think carefully about which conversion rules will be used in a comparison, the language has failed.
The failures of one language’s implementation, however, do not mean that the concepts themselves are flawed. A minor change to the implementation of string conversion in PHP can restore logical consistency to the language.
String conversions are only powerful when unambiguous. It’s inherently useful to be able to make comparisons between numbers and their string representations. It’s much easier to test whether "42" == 42 than whether intFromString("42") == 42, or some other explicit conversion nonsense. I used to believe that the syntactic sugar, the however-many fewer characters, wasn’t worth the logical ambiguity that’s introduced. But I’ve since realized that the trouble isn’t with implicit conversions per se, but with the handling of error cases.
As we saw above, in PHP, any string that can’t be parsed into a number is converted to the number 0. That behavior is wrong. The string "0" is a concrete representation of the number zero, but the string "My bank balance after leaving the Prada store", however descriptive of reality, is not. A string that is not representative of a number should not be converted to any numeric value. Or, put another way, failing silently is the worst way to fail.
JavaScript, for all its flaws, actually gets this one right. It has a special value, NaN, or not a number, and operations that are not defined—division by zero, string conversions that cannot be parsed—result in that value. In JavaScript, there is a one-to-one mapping of numbers to their string representations, and any string that is not a part of that mapping unambiguously belongs to the set of things that are not numbers. Strings can be converted to numbers and back again until the cows come home without any inconsistency.
So it is possible to implement implicit conversions correctly, but as we’ve seen, when the implementation is flawed, language designers patch over those flaws with special cases. These special cases break consistency and defy logic, defeating any usability gains that the feature brought about in the first place. When you have to memorize a table of conversion rules instead of learning a single general and consistent rule, you’re more likely to make mistakes. When a time-saving feature is built wrong, the time saved using it is lost tracking down the bugs that it introduces.
-
A colleague of mine says laziness is a virtue, and he’s got a point. It certainly seems that more new language features satiate programmers’ laziness than appeal to any other trait—as ironic as it sounds, sloth may be the fuel of programming language innovation. ↩
Plus or Minus