I never got the test-driven-development craze until I broke from mostly writing C and C++ and spent some time writing Ruby. This article gets at the crux of that: you must have tests to ensure even the most minimal level of functioning in duck-typed languages. Static typing gives you much of that for free (at, I would argue, very little cost).
Additionally, while the size of my Ruby implementations of components is smaller than their C++ counterparts, the Ruby components plus their tests are larger. (This would be less true were I not to use Qt in most of my C++.)
Tests give you type checking for free. If you have to write tests anyway, why bother writing the type checking twice?
The advantage of static analysis is that it can be used to verify general qualities of your code, but those qualities tend to be very weak relative to amount of extra work required to prove them.
Tests can easily verify highly elaborate specific cases, but generalizing is left to human intuition. A skilled programmer knows which tests are needed to effectively cover all cases. If you are not yet a skilled programmer, or you are working with an unskilled programmer, the tests may be incomplete and it might be worthwhile to use static typing for that small amount of extra coverage.
Tests give you type checking for free. If you have to write tests anyway, why bother writing the type checking twice?
You can reduce the number of tests necessary by designing APIs that simply can not be used incorrectly; it is not necessary to write them to provide type checking "for free".
The question is whether the language provides sufficient facilities to do this concisely, without introducing overhead that outweighs the cost of using an untyped language.
If you are not yet a skilled programmer, or you are working with an unskilled programmer ... it might be worthwhile to use static typing for that small amount of extra coverage.
I see what you did there. I consider hubris a sign of a very unskilled programmer ;)
You can reduce the number of tests necessary by designing APIs that simply can not be used incorrectly; it is not necessary to write them to provide type checking "for free".
But it is necessary to write them to test functionality and if the functionality is correct, surely the types are correct as well (and if they aren't, who cares? It works.)
Static typing makes it marginally safer to not have tests at all, or to have incomplete tests. If you have complete tests, type checking is redundant.
I see what you did there. I consider hubris a sign of a very unskilled programmer ;)
For what it's worth, Larry Wall disagrees with you :)
But it is necessary to write them to test functionality and if the functionality is correct, surely the types are correct as well (and if they aren't, who cares? It works.)
There's less functionality to test if you've described your business constraints in concise type declarations. The type contract is short enough to be validated at a glance.
Static typing makes it marginally safer to not have tests at all, or to have incomplete tests. If you have complete tests, type checking is redundant.
If you have such "complete" tests, you're wasting your time by trying to be a human compiler.
There's less functionality to test if you've described your business constraints in concise type declarations. The type contract is short enough to be validated at a glance.
I'm having a hard time imagining a situation in which static typing could replace even one single test, let alone it being a common occurence. In any such case I can come up with, the test is needlessly limited in scope and can be replaced by a much more comprehensive test. What specifically did you have in mind?
I'd summarize thusly: A powerful type-system provides tools to concisely express business/logic constraints and automatically enforce those constraints across the entirety of your codebase, significantly reducing the testing required to ensure proper operation of your code.
Additionally, while the size of my Ruby implementations of components is smaller than their C++ counterparts, the Ruby components plus their tests are larger. (This would be less true were I not to use Qt in most of my C++.)