Testing is not a Replacement for Static Typing
lovehateubuntu.blogspot.com
lovehateubuntu.blogspot.com
Any algoritm operates and creates state. It is nice your statically typed language prevented you to add your socket file descriptor to an int. So you fix your 'typo' and add the index of the file descriptor (assuming your open sockets live in an array, or similar scenario). But what if your algoritm requires you to do a subtraction? You still have a bug.
No type check can check runtime dependent data. You pass in a List but the list requires to be at least of length 2? (Actually you some languages can tackle this too, but these sort of requirements become arbitrary complex.) You will deref the list out of bounds and you have a bug.
So now I just sketched two scenario's where you still need to actually run your program to catch bugs. And your type system did nothing.
A very certain kind of bug -- mostly typo's or slightly higher level mistake -- are caught by a static type systems. The question is, is this worth the restricted semantics of the language?
The advantage of a static type system is clear; these mistakes are caught where and when they are introduced, not where their result is used.
The disadvantage is also clear: restricted semantics. How would macro's look for lisp if it had type checks?
Without types, your language is better at creating macro-like abstractions. You can capture patterns with these, and prevent a lot of typos that way. (And a write a lot more concise code.)
And even more, think about Java's performance hit on ArrayStoreException ... So even such statically typed languages need to do runtime type checking more often then you would like.
In the real world, static and dynamic typing both have their place. Different problems have different tool requirements. There is no One True Language and there never will be. For most of the things I do, I can make do with four.
Static Typing vs Testing: Which is better? No! Stop that! There is no "vs"!
To take Haskell as an example, it is a completely statically typed language which, if not abused cleverly, guarantees that you don't have code trying to add inches to centimeters, or divide a hangar by a sorted apple. It also has a very powerful unit test generation and execution library (+), code coverage tools, and test statistics tools. These libraries and tools are frequently used by Haskell programmers. Creating any large project without unit tests is just as silly in Haskell as it would be in Python.
Nobody has ever (or possibly, only nobodies have) argued that static typing eliminates the need for tests. The author of this post didn't argue that either.
(+) http://www.haskell.org/haskellwiki/Introduction_to_QuickChec...
Everybody says this about Haskell, and it's probably true.
But really, isn't it about as true of Lisp, Scheme, or Clojure? Once you can call your program in a repl, it's probably mostly right. Maybe it's just my inexperience with Haskell showing, but ISTM that the square-peg-round-hole errors catchable by type checking are pretty rare.
Am I missing something?
As far as semantics, GHCi doesn't let you redefine a function and continue working, largely, I suspect, because Haskell doesn't describe how that should work.
Conversely, if you keep your source open in an editor and reload, then you can change classes, macros, etc, but you lose your state across reloading (declaring top-level values to set up your testing state can help, but it's less fluid).
Type checking is still in effect during long-term maintenance.
If you do (and the errors are not spurious), then the static type system is catching real bugs that would have survived for some length of time in a dynamically typed language.
Then there's the data-processing-heavy backends where you need to do a lot of parsing, a lot of analytics, a lot of processing, etc... static typing helps a lot here. You do not want to make silly stupid mistakes when you're parsing one value to another, and types help make sure you're getting the right inputs and outputs. When you're doing your metrics, you often don't even want to be accidentally mixing your floats and ints. If you want to get real fancy about it, monads and functors can help immensely for managing state and joining data for a lot of these backend problems.
The folks who don't see this generally haven't had the good luck to work on big problems throughout the stack to see where the strengths of each paradigm come into play. There's plenty of functional programmers who don't know how to exploit a dynamic language (stop trying to impose types on the system dammit!) and vice versa.
If all else were truly equal, Britton would be right -- static typing clearly would be better. (I think. It's a question I wrestle with quite a bit.) But all else isn't equal [], so the static vs dynamic distinction is just one of many aspects to take into account when choosing a programming language.
Please correct me if I'm wrong: if there's a language that has the feel and rapid turnaround of a dynamically typed language with type checking performed early, I'd like to know about it! The closest I've used is probably Scala, maybe OCaml (but I've only studied it briefly, and I've certainly never mastered it, alas).
I say that static typing is like a game where you have to push square pegs into various holes that aren't square. You say that dynamic typing is like opening the flood gates on bugs.
Oddly enough, we're both right and both wrong at the same time.
This is definitely debatable, but I think we should agree to disagree at this point.