When I first discovered dynamically-typed languages like Perl and Ruby, I was convinced of their superiority to statically-typed languages like C++. No longer did I have to waste hours typing redundant type declarations or adding casts just to make the compiler happy. Dynamic typing allowed me to work quickly and unencumbered in what felt like a natural manner.
Lately, however, I’ve been experiencing some of the drawbacks. Many times I have started a long batch process running in Ruby, only to come back hours later to find that it crashed before getting halfway through because I made a mistake. Often, the mistakes were simple typos or misspellings. Sometimes they were the result of sloppy code, like not checking if a returned value is nil.
Because my code isn’t error-checked before it runs — in Rails, some code doesn’t even get syntax-checked before it runs, thanks to lazy autoloading — my mistakes don’t manifest until hours or days into a process. Then I have to fix them and start the process over.
Now I realize while statically-typed and -checked languages were so important in the pre-PC era. If you had a turnaround time of several hours between submitting your program for execution and receiving the results, you would want to be certain that it would work correctly. The instant feedback of the write-compile-run (or, with an interpreted language, just write-run) cycle makes it easier to be sloppy.
A static compiler or syntax checker would probably catch most of the mistakes I just described. So would proper unit tests. But the ability to constantly restructure large sections of code as I explore the problem — an ability granted by an interpreted, dynamic language — also makes it hard to keep tests up-to-date. Writing tests comes to seem as tedious and unhelpful as type declarations.
What I really want is a “magic” compiler that can look through my code and point out all the mistakes. A combination of good unit tests and a test coverage tool would come close, but that requires a lot of extra effort on my part.