Hacker News new | ask | show | jobs
by jdm2212 2152 days ago
The study IMHO is reality. Every large, actively maintained system I've ever heard of uses a strongly typed language, or was written in a dynamically typed language but has converted to a gradually typed language. You can't safely refactor without compile time type checking, and you can't maintain a non-trivial system over the long-term if you can't refactor it.
2 comments

A contrary anecdata - I've worked at a bunch of places and know people who work at others that have large, actively maintained, decades-old systems using, e.g., Perl. The only one I know that was actively trying to migrate recently was looking at node instead.
Just write tests and suddenly refactoring is possible without static typing. Those tests will also cover type checking concerns implicitly.
Tests can't replace types, just like types can't replace tests. You need both.

Types can't check the correctness of everything, but they do prove that certain classes of errors don't exist in your program.

Tests, on the other hand, can test for many more types of bugs, but they can only look for errors, they can't prove correctness (except in very small, closed environments where you can literally test every possible combination of inputs and outputs).

> they do prove that certain classes of errors don't exist in your program

That's particularly important when refactoring because you want to assert that you haven't introduced new bugs, and the type system will often let you prove that with almost zero effort on your part.

Yeah, except your tests can never prove that your code is fully sound, whereas a good type system can (for some definition of soundness)
If you add a new value to an enum, or a new argument to a method, you have to know all the places where you do switches on the enum or call the method, which is hard to do without static types.
Why couldn't you just search for where that enum is referenced and add the cases?
You have to know when semantically it's an instance of the enum, and when it's just a string literal (e.g. in JavaScript, where enums are just a pre-defined set of allowed strings). Also, you might assign to it in one place, and then use the resulting variable in many other places farther down the call chain. Now you have to find all those usages.

Static typing makes both of those trivial if your language (or linter) has enum-exhaustiveness checks for switch statements.

You can't always find everything with a search in a dynamic language. Some things are resolved at runtime. In our Perl code base, finding function calls is difficult for this reason (you can eval a string name to get a module or function and call it, based on a configuration setting in a file).
Repeat after me:

Tests are existentially quantified, types are universally quantified.

(Yeah, existential types, I know. Shut up. ;) )