Unit testing alleviates most of my needs for static typing.
— Joe Barnes (@josefusbarnabas) September 7, 2012
I hereby retract that statement.
First off, at the time I had not started playing with functional languages and I had no idea what a legit type system was. As Paul Callaghan put it, you need to “put aside your bad experiences from Java”. I was at the peak of my Groovy enthusiasm. Now that I’ve spent ample time in Scala, I realize that a static type system doesn’t have to suck. While I once would have agreed with Martin Fowler that dynamic typing makes programming fun, I much more enjoy the secure feeling that a solid type system gives me (BTW, I can’t help but notice that he doesn’t mention any languages written by academic type theory junkies).
1. Catch the bugs earlier
If it’s true that the earlier you catch a bug, the cheaper the bug costs, then I can think of nothing better than compile-time checking. Many bugs enter the code the moment you type it (that is, bugs that are introduced during coding after spec/design). Now you’re on the clock to find them. If you’re working with a dynamically typed language, maybe you can run your unit tests in the time a compiler runs. But let’s not forget that the type checker can even run in an IDE, finding bugs as soon as they are typed!
2. A good static type system is free, but unit tests are costly.
The fact is, if you want to use unit testing, you have to write the unit tests! Now I know someone has to write the type system, but my point is that YOU don’t have to. Maybe static type checker doesn’t cover all bugs (a debatable point), but it will have 100% coverage over your code. There’s no such guarantee for unit tests. You’ll have to find a coverage tool, and not to mention that 100% coverage can be achieved with useless unit tests.
3. A type checker will pinpoint exactly where you broke it
In my spare time project, I’m learning a lot. I mean a LOT. Unfamiliarity with a technology is a heavy factor for my selection process, allowing me to learn new stuff en passant to producing a product I’d like to use. This results in lots of rewrites. Just yesterday I had a class that I was modeling as an embedded document and realized it would be better off as a reference in my MongoDB. When I make a change to the API of a class/trait, I love how the compiler points at every usage of that code that is now broken. If I were to rely on unit testing, I would only know that my code is in fact broken (duh!). I would also know what functionality broken, but I would not know immediately where it is broken. So if you do a lot of refactoring (the other pillar of Test-Driven Development!), then I’d argue you’ll refactor faster with a good statically-typed language.
I don’t believe that static type checking catches all bugs, but neither do unit tests. Furthermore the notion that type checking can be set aside for unit testing isn’t supported by data. However, a functional language with a good type system will make even the venerable Kent Beck rethink TDD. Software quality is a complex problem, and there’s no sense in throwing out one tool for another when you can have both. This isn’t to say that you’re stupid for using dynamically typed languages, but the benefits of static type checking have won me over.
My next blog post will highlight some of the Scala language features that helped flip my position. And thanks as always for reading my blog, Mom.