In Favor of Optional Type Safety

"… my experience with OCaml was mostly pleasant (indeed, voluntary!), though over time I've come to favor languages which let me make more mistakes."

I wrote that in an e-mail to a professor who asked my opinion of OCaml. I wanted to write more, but even though I knew it was true, I couldn't explain why I felt that way… until now.

I realized that I manage the complexity of hastily-crafted prototypes (that's most of the code I write) by maximizing the amount of dead code. That sounds weird, but it makes sense, and maybe you do it too.

I consider class and method definitions in Ruby to be dead. They don't do anything by themselves. It's not until you call into that code that it has life—and if the code is modular, that effect doesn't cascade very far. So when I write mini tests at the bottom of the file, the dead code lights up like synapses firing in the brain, the program counter carving a path through some relevant subset of the code.

And when I decide upon a change I want to make, I usually don't bother finding all of the places that need to be updated. I just make the change and keep rerunning the tests I care about until the parts I'm interested in work again. So the more mistakes the language allows me to leave in dead code, the faster I can make changes.

Now, what I've been calling "dead" code isn't completely dead; it still needs to be syntactically correct enough for Ruby to skim past it (bleh), but Ruby's bar is far lower than the bars of languages which perform type checking. And it's lower than the bar for unit-tested Ruby code: good tests maximize the amount of live code, which requires that every change be reflected in the entire system at all times! What a deterrent to change.

So until there's some reason to keep all the lights on, I don't! And until mistakes matter, I will make them!

"… some still seem to equate 'the ease of programming' with the ease of making undetected mistakes." —Dijkstra

Hold the phone!

The original version of this post ended with the following postscript (the emphasis is new):

P.S. Once the time for mistakes has passed, type-checking can be a godsend. It's like the compiler writing unit tests for every line of code for you (because it is), and in several ways those tests make stronger guarantees than you could ever make with unit tests you write yourself. It's too bad that languages don't let you turn off type-checking with a flag.

Somebody read that and referred me to a paper by some researchers who share my sentiment exactly: "Always-Available Static and Dynamic Feedback." They posit that "a developer should write code in a statically-typed language, making a best-faith effort to get the types right and obtaining sound feedback from the type checker. But, even at moments when the program is not globally type-correct, the developer should be able to run the code to test it or to increase insight." They implemented a transformation for Java programs which retains type information but does all type checking at run-time, so that you can run a Java program in which some of the types may be incorrect. When your program's types stabilize, you can turn compile-time type checking back on. It's perfect!

Did I level up with this post?


Comments

Click here to view the comments on this post, or just send me an e-mail.