In order to catch problems in dynamic type languages you end up needing a bunch of additional tests to, ironically, verify the expected type. And even then, those tests don't and can't tell you how a method is actually used throughout the program.
Consider the following class
class Human
def initialize(name)
@name = name
end
end
Now imagine you want to refactor this class to something like this class Human
def initialize(first, middle, last)
@fullName = "#{first} #{middle} #{last}"
@first = first
@middle = middle
@last = last
end
end
All the sudden you've got a problem on your hands. You have to find everywhere that referenced `name` on a `Human` object (And don't mess that up, it could be `name` on the `Pet` object) and change them over to full name, or figure out if they are doing something tricky like trying to extract the first name from `name`.Types make refactoring this sort of code somewhat trivial. You simply change the fields and let the compiler tell you ever position which relied on that field.
This extends into all sort of circumstances, like letting the user of a method know what types are expected. For example, the above ruby code doesn't really guarantee that `first` is a string. In fact, it could be an int or even an object and the code would likely handle it just fine. Types expose interface contracts in a quick and easy way for programmers to understand what they are dealing with.