Great link, a recommended read.
I love the puzzle argument. It aligns perfectly with the research on productivity of languages and language paradigms I’ve read about. It was found that people using dynamically typed languages took less time, but when interviewed, the people with the static type languages felt like the type system made them more productive, and they’d want to keep using static types. While the dynamic ones felt either neutral, or felt like they were slowed down by having to chase down type errors.
This means static typing often makes you feel smart and productive. And I think its that puzzle solving. It’s rewarding and fun, but it’s not progress towards your real problem. While in a dynamic world, you always feel a little more lost, and you’ve got to explore and think harder about what is what, but you’re always focused on the real problem, you might feel confused, but if measured, you are still more productive and turn out equally good software, i.e., equal number of defects in the resulting product.
And that’s my biggest issue with static vs dynamic. No one is being practical and honest about the benefits. Many people say its safety, to have less bugs. Alright, but that’s never held up in empirical studies. In fact, Clojure outperformed many statically typed languages, ranking in the top 3 of lowest defect rate.
Here’s things that empirically demonstrate real defect reduction value:
- Runtime type checking
- Strong typing
- Functional programming
- Unit tests
- Integ tests
- Performance tests
- Generative tests
- Fuzzing tests
- Declarative programming
- Automated memory management (like GC)
- Continuous integration, delivery and deployment
- Code reviews
- Formal verification
Clojure offers all of these, except the last one. Though formal verification is often orthogonal, and proved in a parallel language.
You’re better off adding one of these to your development process, than going for static typing, if you’re looking at outputting safer software.
Alright, so what else are static types beneficial for? Some say it makes things easier to read. But I’ve always found this to be a fallacious argument. How easy it is to read and understand a program’s code is not a relevant metric. All that matters is speed of delivery, cost, reliability, safety, performance and scale. No one pays you to have an easier time. How quickly is it for you to add features to an acceptable level of quality, at an acceptable price for the given usage. Where quality are the prior metrics: reliability, availability, safety, performance, scale, completeness and user acceptance.
In that respect, my search for empirical data also only showed me that static types make you less productive, and the only quality metric they affect is performance. Clojure has static types for performance, in the form of type hints.
This is the puzzle thing again. You personally might feel like a code base is messy, hard to follow and understand. But how much time (not mental effort) did it really take you to add the feature, or fix the reported issue, and does it work?
Again, I feel with static types, you have a false impression that you follow along more easily. But its because you quickly figure out what types things are and how they connect. Yet you havn’t understood anything of the architecture, logic and behaviour of the code appart for its type interaction. This feeds into your “easy” impression. But probably doesn’t affect your productivity at all.
I’m not against static types. But I’m honest about there value. They are fun to play with, and I love learning about their cleverness and beauty. I’m unsure they add anything more, and if they did, it must be a very small benefit, that is almost within the margin of error.
Now RUST is king for me in the static type languages. To statically prove proper memory management, while preventing the overhead of a runtime GC is a real concrete benefit. Proving the lack of deadlocks and race conditions in parallel code is another one. These kind of defect prevention is much more practical and beneficial from my perspective, then realizing 10 minutes earlier that you passed a string to a function expecting an int.