> they're also a way of defining and enforcing at least parts of the programming contract.
Violation of those parts of the contract are type errors. (And all proper type errors—those that aren't artifacts of the type system and it's incorrect use or impedance mismatch with the project—are violations of the programming contract.
> The time you save from writing a dynamic program is time not spent on defining that contract.
No, you can still define the contract when using a dynamic language, and still often save time compared to a real static language.
In the ideal case, sure, a static language would add no additional overhead to this, but that's an unattainable ideal.
I think you're maybe overly focused on the "compile time type checker" bit of static typing.
The contract definition bit is secondary, but useful. It's also at least somewhat separable from the type checking.
The best example I can think of is to compare the duck typing that is common in many dynamic languages with the formal interfaces that are more popular in static languages. With duck typing, you basically look for a method with a particular name in an object, and then, having found it, simply assume that that method is going to implement the semantics you expect. That works surprisingly often, but it is a bit rough-and-ready for many people's tastes.
With formal interfaces, you have a clearer indication from the type's author that they're consciously planning on implementing a specific set of semantics. They could still be doing it wrong, of course, but it's at least trying to be more meticulous about things.
I also think it's worth pointing out that static typing can be useful as a performance thing. Pushing all those type checks into run-time does have costs. There's the extra branch instructions involved in doing all those type checks at run time, and there's the extra memory consumption involved in carrying around that type information at run time.
(It's also true that this aspect is very much a continuum, especially with regards to the performance considerations: Many dynamic languages have JIT compilers that can statically analyze the code and treat it as if it were statically typed at run time, and any ostensibly static language that allows downcasting or reflection supplies those features by carrying type information and allowing for type checks at run time.)
Yes they are.
> they're also a way of defining and enforcing at least parts of the programming contract.
Violation of those parts of the contract are type errors. (And all proper type errors—those that aren't artifacts of the type system and it's incorrect use or impedance mismatch with the project—are violations of the programming contract.
> The time you save from writing a dynamic program is time not spent on defining that contract.
No, you can still define the contract when using a dynamic language, and still often save time compared to a real static language.
In the ideal case, sure, a static language would add no additional overhead to this, but that's an unattainable ideal.