Jay Taylor's notes
back to listing index5-2-03 Strong Typing vs. Strong Testing - Google Docs
[web search]
Original source (docs.google.com)
Clipped on: 2014-12-27
To enable screen reader support, press shortcut ⌘+Option+Z. To learn about keyboard shortcuts, press shortcut ⌘slash.
5-2-03 Strong Typing vs. Strong Testing
(A more accurate title would be "Static Checking vs. Strong Testing").
In recent years my primary interest has become programmer productivity.
Programmer cycles are expensive, CPU cycles are cheap, and I believe that
we should no longer pay for the latter with the former.
How can we get maximal leverage on the problems we try to solve?
Whenever a new tool (especially a programming language) appears, that
tool provides some kind of abstraction that may or may not hide needless
detail from the programmer. I have come, however, to always be on watch
for a Faustian bargain, especially one that tries to convince me to ignore all
the hoops I must jump through in order to achieve this abstraction. Perl is
an excellent example of this — the immediacy of the language hides the
meaningless details of building a program, but the unreadable syntax
(based, I know, on backwards-compatibility with Unix tools like awk, sed and
grep) is a counterproductive price to pay.
The last several years have clarified this Faustian bargain in terms of more
traditional programming languages and their orientation towards strong type
checking. This began with a 2-month love affair with Perl, which gave me
productivity through rapid turnaround. (The affair was terminated because of
Perl's reprehensible treatment of references and classes; only later did I see
the real problems with the syntax.) Issues of strong-vs-weak typing were
not visible with Perl, since you can't build projects large enough to see these
issues and the syntax obscures everything in smaller programs.
After I had worked with Python (free at www.Python.org) for awhile — a
language which can build large, complex systems — I began noticing that
despite an apparent carelessness about type checking, Python programs
seemed to work quite well without much effort, and without the kinds of
problems you would expect from a language that doesn't have the strong,
static type checking that we've all come to "know" is the only correct way of
solving the programming problem.
This became a puzzle to me: if strong static type checking is so important,
why are people able to build big, complex Python programs (with much
shorter time and effort than the strong static counterparts) without the
disaster that I was so sure would ensue?
This shook my unquestioning acceptance of strong type checking (acquired
when moving from C to C++, where the improvement was dramatic) enough
that the next time I examined the issue of checked exceptions in Java, I
asked "why"? which produced a big discussion wherin I was told that if I
kept advocating unchecked exceptions, cities would fall and civilization as we
know it would cease to exist. In Thinking in Java, 3rd edition, I went ahead
and advocated the use of RuntimeException as a wrapper class to "turn
off" checked exceptions. Every time I do it now, it seems right (I note that
Martin Fowler came up with the same idea at roughly the same time), but I
still get the occasional email that warns me that I am violating all that is
right and true and probably the USA Patriot act, as well (hi, all you guys
from the FBI! Welcome to my weblog!).
But deciding that checked exceptions seem like more trouble than they're
worth (the checking, not the exception. I believe that a single, consistent
error reporting mechanism is essential) did not answer the question "why
does Python work so well, when conventional wisdom says it should produce
massive failures?" Python and similar "weak" or "latently" typed languages
are very lazy about type checking. Instead of putting the strongest possible
constraints upon the type of objects, as early as possible (as C++ and Java
do), languages like Ruby, Smalltalk and Python put the loosestpossible
constraints on types, and evaluate types only if they have to. That is, you
can send any message to any object, and the language only cares that the
object can accept the message — it doesn't require that the object be a
particular type, as Java and C++ do. For example, if you have pets that can
speak in Java, the code looks like this:
// Speaking pets in Java:
interface Pet {
void speak();
}
class Cat implements Pet {
public void speak() { System.out.println("meow!"); }
}
class Dog implements Pet {
public void speak() { System.out.println("woof!"); }
}
public class PetSpeak {
static void command(Pet p) { p.speak(); }
public static void main(String[] args) {
Pet[] pets = { new Cat(), new Dog() };
for(int i = 0; i < pets.length; i++)
command(pets[i]);
}
}