Comment by wildermuthn

5 years ago

Actually, I found the post you linked to fairly logical. He’s saying that humans are inherently lazy, and that a language that gives us the option between being diligent (strong types) or being reckless (opt-out of strong types) will lead to the worst form of recklessness: opting out while not writing tests, giving the misimpression of safety.

His point is that you can’t practically force programmers to be diligent through safety features of a language itself, since edge-cases require escape hatches from those safety features, and those safety hatches will be exploited by our natural tendency to avoid “punishment”.

I’m not sure I agree with his point, but I don’t find it an unreasonable position. I’d be curious if Rust has escape hatches that are easily and often abused.

My favorite example here, and a counterpoint to Bob, is Reacts’s dangerously-unsafe-html attribute. I haven’t seen it in years (to the point where I can’t recall the exact naming), and perhaps it was removed at some point. But it made the escape hatch really painful to use. And so the pain of using the escape hatch made it less painful to actually write React in the right manner. Coming from Angular, I think I struggled at first with thinking I had to write some dangerous html, but over time I forgot the choice of writing poor React code even existed.

So I guess I disagree with Bob’s post here. It is possible to have safety features in languages that are less painful than the escape-hatches from those safety features. And no suite of tests will ever be as powerful as those built-in safety features.

He actually misunderstands and mischaracterizes the features of the languages he complains about. These features remove the need for a developer to keep track of invariants in their code, so should be embraced and welcomed by lazy developers who don't have to simulate the boring parts of code in their head to make sure it works. "If it type-checks then it works" philosophy really goes a long way toward relieving developer's stress.

For example, if I'm using C or Java I have take into account that every pointer or reference can be null, at every place where they are used. I should write null checks, (or error checks say from opening a file handle) but I usually don't because I'm lazy, or I forget, or its hard to keep track of all possible error conditions. So I'm stressed during a release because I can't predict the input that may crash my code.

In a language like Swift I am forced to do a null or an error check once in the code, and for that small effort the compiler will guarantee I will never have to worry about these error conditions again. This type system means I can refactor code drastically and with confidence, and I don't have to spend time worrying about all code paths to see if one of them would result in an unexpected null reference. On a professional development team, it should be a no-brainer to adopt a new technology to eliminate all null-reference exceptions at runtime, or use a language to setup guarantees that will hold under all conditions and in the future evolution of the code.

Worse than that, he sets up a patronizing and misguided mental image of a common developer who he imagines will use a language with type safety just to disable and abuse all the safety features. Nobody does that, in my experience of professional Swift, Kotlin or rust development.

He advocates for unit tests only and above all else. That is also painfully misguided: a test tells you it passes for one given example of input. In comparison a good type system guarantees that your code will work for ALL values of a given type. Of course type systems can't express all invariants, so there is a need for both approaches. But that lack of nuance and plain bad advice turned me into an anti-UncleBob advocate.