I was reading through Bruce Eckel's Static vs. Dynamic, which repeats a message we see many places in many forms: This last point is a major puzzle -- we believe that static type checking prevents bugs, and yet a dynamically-typed language produces very good results anyway.

There's a couple reasons, like a good language for rapid prototyping leaves you more time to debug, or more time to use good practices early on (like unit testing and continuous refactoring). But then it might just be a wash, if it leaves us more time to fix our problems, it still might be the same distance to "finished". I think this is the case for Perl and PHP, but not for other dynamic languages like Python, Smalltalk, Ruby, Lisp.

The difference being a really high-level language, and just a dynamic language: I don't think Perl and PHP are really high-level. Which is to say, there is a limit to how far you can build abstractions. Regular expressions are an excellent example. They are really nice, especially if you compare it to C string handling. But once you have them, they don't lead anywhere. They are one tool among many, and while they are useful, at some point programs need to move to a higher level where that kind of string manipulation is an incidental (and replaceable) implementation detail. So Perl seemed hot when regular expressions were cutting edge, but it loses momentum now that they are widely usable in all decent programming environments.

When reading Phillip's Python Is Not Java, the basic message is really: aim higher. You've been given a high-level language (finally!), so make use of it, otherwise the improvements will only be nominal.

Back to reliability: one way to decrease bugs is testing, but another way is to decrease the amount of code. Code deleted is code debugged. Static typing can decrease the number of bugs, but decreasing the amount of code is a much, much more effective way to decrease bugs. If you can have both -- short code and static typing -- then more power to you. I just haven't seen it myself.

I think this is in part because types aren't actually that interesting. People often talk about the four attributes: dynamic typing (Python) vs. static typing (Java), and weakly typed (C) vs. strongly typed (Python or Java). Now forgotten, at one time there were languages that were both weakly typed and dynamically typed; things like assembly and Forth. Ouch. There were two paths from there: one to make runtime values intrinsically typed (Lisp), and one to make the source code intrinsically typed (C). But just because assembly sucked a lot doesn't mean all languages afterward must approach the problem in terms of its suckage. And just because there was that split, doesn't mean joining both worlds (a language with both intrinsically typed values and source, i.e. Java) is better. And luckily you can't take intrinsically typed values any further; once you've accomplished it, your work is over, values can't be more intrinsically typed. Intrinsically typed source is really hard, and it's an unfinished job; ML, Haskell, and their progeny continue the work, but it's clearly not finished.

So here we we are, the dynamic languages have finished the work on types (at least the work we choose to do). We moved on a long time ago (well, they moved on with Lisp when I was still a child, long before I could consider myself a member of any we), and we've been moving on to other techniques of higher level programming while statically typed languages have languished. That is why I believe dynamically typed languages are better, simply because it addressed a real problem (weak dynamic languages) in a solvable way.

Static typing won't lead to less bugs, because hard problems are still hard. Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp (source). In part that's a defense of a big standard library, but it's also a defense of high level programming. Low-level languages don't just reimplement specifics, like hash tables or regular expressions, but there's a loss of many abstractions because you can't express some concepts in low-level languages in a way that is reusable. Static type defenders complain about the combinatorial type interactions in dynamically typed languages -- how can you test all the code paths, given all the possible types? But their languages lead to combinatorial code, which is far worse: for many classes of hard problems, they can't solve them once, they have to solve them everytime they are encountered. Ouch. Dynamic typing isn't the solution, it's just the best place we have to start from if we're looking for a solution.

Maybe part of the problem is a lack of imagination. People ask which system is the right system? when the answer should clearly be none. Rather we must ask which system has the most potential for leading us towards the right system? To advocate any system as The Right Way is as absurd as it is obnoxious; as long as there are unanswered problems we haven't figured out the Right Way.