Nobody code without a type system. The distinction is build time vs runtime type checking. At build time you catch bugs that would appear later at runtime. Which is more costly to fix later.
This assumption is changed, IMHO, by Erlang. Hot loading makes the cost to make small changes very low. So the question becomes, do you pay the definite cost of build time type checking (usually includes coding time type annotation), or do you accept the possible future cost to making small fixes.
Of course, if you work in an organization where even a small fix requires months to release, then do all the things you can to prevent making small mistakes.
The cost is also in debugging. It’s much harder to figure out a problem after the fact because you’ve forgotten how the code in question works. If you catch a mistake while you’re coding something up, you can fix it immediately and not give it a second thought. But if you need to track a bug down weeks or months after writing the code, it can take a lot of work to figure out what the code does (and why), and why it is behaving incorrectly.
I’ve lost weeks to a memory leak once in javascript that was a 2 line change to fix. If I realised the problem when I wrote the code, I would have saved myself a lot of trouble.
Essentially every statically typed language contains runtime errors that can be costly too.
Erlang was designed to achieve 9 nines of uptime. It achieved this without static typing across very large applications. The fact that this is a regular occurrence with Erlang disproves the idea that a lack of types is fundamentally unsafe.
Static types are most useful with monolithic application design. They counter your massive ball of code growing too complex and the complete lack of introspection at runtime. They attempt to handle the problem that any error crashes your entire system.
Erlang uses a different approach.
First, it uses safe datatypes. You aren’t going to crash because you chose a 32-bit integer and rolled it over (something a type system won’t actually help with). This is maximally likely to corrupt user data without even raising any flags. Integer rollover has actually killed people (famous in the Therac-25).
Second, it uses all immutable data, so data sharing is safe. Also something types don’t help with. This is also a maximal risk of data corruption. Incorrectly mutating data has also killed people (also in Therac-25).
Third, it is functional. Toys reduces passing around giant balls of mud. Those balls are unusable disasters unless you add some types. Functional programming with immutable records means that a program can’t accidentally change the types of incoming data. Because the pattern encourages separating data and mutable state, the most common typing accidents are simply avoided.
Erlang is designed with concurrency first. This helps to keep those balls of code even smaller and further reducing the chances of typing errors. And of course, combined with immutable data, we eliminate another set of errors typing does nothing about and that have caused massive damage and probably deaths (a deadlock causing a NYC blackout leaps to mind).
Finally (I probably missed some points), Erlang is designed expecting crashes to happen. Few runtimes are capable of anything close to the elegant Crash handling of BEAM. Instead of fearing crashes, you understand they’re inevitable and embrace them. This means that you are prepared for not just an occasional type error, but will also elegantly handle null exceptions that plague most of the most common statically typed languages.
A small number do. Assembly languages are generally untyped. The Forth language is also untyped.
> At build time you catch bugs that would appear later at runtime. Which is more costly to fix later.
Generally agree. Programmers proficient in Haskell or Ada tend to consider types to be integral to their development process. The real question is whether this is a good tradeoff against development velocity, for your given project. Neither language markets itself for rapid application development, instead they tend to emphasise that the language aids with correctness and the ability to reason about code's behaviour.
This assumption is changed, IMHO, by Erlang. Hot loading makes the cost to make small changes very low. So the question becomes, do you pay the definite cost of build time type checking (usually includes coding time type annotation), or do you accept the possible future cost to making small fixes.
Of course, if you work in an organization where even a small fix requires months to release, then do all the things you can to prevent making small mistakes.