I was having a discussion with a friend of mine not too long ago debating the merits of dynamic languages. My friend believes that type-safety is a top priority and that a language that does not do compile-time (or for an interpreted language, parse-time I suppose) type checking is a disaster waiting to happen.
This is completely correct. Unless you're doing something trivial like a script to resize/rotate the pictures from your digital camera, a dynamically-typed language will have problems when the app gets more complex. For example, suppose while refactoring you want to change the parameter list of a function for some reason. Maybe some parameter is no longer needed, who knows. In a statically typed language, this is not a huge issue because the compiler will pick up all the places that the function is called and tell you if there are problems. Not so in a dynamically typed language, instead you have to actually execute the code. If your program is highly interactive, this will lead to problems because you will manually have to execute each version and catch the error at run-time. And if you forget to check something? Well, your users will find it.
Here is where the standard response comes in from dynamic-typing enthusiasts: testing suites. All non-trivial programs should have testing suites, regardless of the language. No programming language, even Haskell with its crazy type system, will save you from the requirement of clarifying your ideas - at least you better hope so, or you're out of a job. A type-checker can tell you if the types you are throwing around make sense, but they don't check whether the code is working the way it is supposed to. For example, one time I wrote a 3D vector class in C++. I made a typo in the cross product function and put a - instead of a +. Passed the type check just fine, but when the shading on objects was looking horribly fucked up, I had absolutely no idea what was going on. This kind of bug can be easily avoided by a simple unit test.
A good unit testing suite will help a lot, and catch many of the errors that would normally be caught by a compiler (like type-errors or spelling mistakes) while at the same time verifying the behaviour of your code.
Unfortunately, the type-safety depends on the quality of your unit tests. It is prone to human error. It is this fact that I believe a program written in a dynamically-typed language can never be as type-safe as one in a statically-typed one.
You could work around this by checking the types of the arguments at the start of every function. However this is paranoid. If you're doing this, why don't you just switch to a statically-typed language which will do this automatically, more quickly, and with less work for you?
So we come to a trade-off. Many programmers discover quite quickly that a dynamically-typed language will let you get code out the door much more quickly than with a statically-typed one. I've been mucking around with a gaming library in Python (I'll post about this another time) and I'm finding it is much easier to get simple stuff done than I ever did with C++. In fact it is so simple that there is no need for adding scripting languages on top, or config files, or all that. I can just write the stuff in Python. I've had similar experiences with Ruby.
But to get these benefits, you must accept the fact that you will not be having 100% type-safety. There may have to write more tests than you would have.
Is it worth it? Sacrificing type-safety for faster development times? Well, it depends. In fact, you may not always have faster development times. I believe that there is a threshold at some point where a dynamically-typed language will have slower development times than a statically-typed one. This threshold is dependent on many factors - how good the design is, how good the programmers are, how many programmers there are, how big the project is, how quickly requirements change, etc.
Finally one last thing to note is that statically-typed languages are easier to optimize for speed than dynamically-typed languages. In a statically-typed language, the compiler knows without a doubt the type of a variable, and can sometimes drop the type information from the resulting machine/byte code. The compiler knows at compile-time whether an object has a certain method or not, so there is no need to check at run-time, or even keep track of what methods an object really has. A lot of this is why C and C++ are still faster than well, anything else (of course there are other factors, like how most dynamically-typed languages these days are interpreted).