For a one-time strong static type snob, I've become pleasantly enamored with Python lately. Seriously – it's a Lisp for a new generation. I remember when I first heard about Python, people seemed to place it in the same class as Perl (so-called scripting languages) and since I already let Perl taint my brain, I figured one was enough. That was a mistake.
What really made me take a closer look at Python was the object-relational mapping in Django. Specifying data models in Django is natural, convenient, powerful and – get this – there's no code-generation step. No turn-the-crank-and-output-a-mess-of-code tool to add to your Makefile. Advocates of Domain Specific Embedded Languages in Haskell might even be pleased, except that it doesn't take three Ph.D. dissertations' worth of language extensions to satisfy the type checker. (Ouch – did I just bite the hand that fed me? Whatever, it's a ’blog… the medium practically invites polemics.)
Anyway, test-driven development (TDD) is a necessity for dynamic languages. When a typo in a method name has no ramifications until run-time, there had better be some unit tests exercising that code frequently and automatically. Once you have the framework in place, it's a baby step to writing a test that fails before you code the feature that satisfies it.
I ought to take it further. I'm not yet coverage-testing. Ideally, commenting out any single line of code should cause some test to fail. Even changing a ‘<’ operator to ‘≤’ should cause some test to fail. That's how you get to more lines of test code than normal code.
And now Dijkstra's spirit is looking over my shoulder, muttering something about “hopelessly inadequate”. Oh well, maybe in a few weeks I'll be hacking together something in Scala or proving something in Coq, and I'll change my mind about static type discipline again.