Funny. I feel exactly the opposite. Static typing is false security.
I program in JS. My coworkers in Java. They have so many bugs that make it to prod because they assume that just because it compiles it must therefore be safe.
Funny. I feel exactly the opposite. Static typing is false security.
I program in JS. My coworkers in Java. They have so many bugs that make it to prod because they assume that just because it compiles it must therefore be safe.
In the former languages, you can encode many more attributes of a program into the type system, and so you can have check at compile-time many more invariants of your program - e.g. enforcing a function does no I/O, enforcing that all possibilities are handled when pattern matching, enforcing the equivalent of nullptr checking, etc.
In languages like C++, there are techniques you can apply to allow the type system to help you out a bit (see this talk by Jordan DeLong for some examples [1]), but in general, the guarantees you get from a non-HM type system are weaker than those from one, and so the notion of successfully passing type-checking is correspondingly weaker.
Good type systems can catch far more bugs than you imagine. Moreover, they can actually make writing code easier: there are some extremely valuable and expressive features like typeclasses that simply cannot be reproduced in a dynamically typed language.
The problem is not how to make something compile, regardless of how complex the types are. The problem is how to do the right thing. It is not enough to return a string - you must return the correct string, and that is not normally not something a type can solve for you. I suppose you may solve that problem too in haskell, but then you will get bugs in the type definition instead. TANSTAAFL.
The key point is that a sufficiently expressive type system gets you far closer to correctness on the basis of type checking alone than does a weaker static system like Java's or a dynamic one like JS's. You should still write tests, you just won't have to write as many in order to reach the same degree of confidence in your code.
It can, however, ensure that you have returned a legally-formatted email address instead of someone's first name, with proper use of the type system. You'd probably be surprised what you can do with a real type system, based on what you're saying here. It is not a proof of correctness, but there's a great deal more possible than in Java.
It of course can not ensure you returned the correct email address, but often that's a much less pressing problem, because the thing returning the address may very well have had only one address in hand to chose from, in which case it can't be wrong. There's a lot of logic like that you can deploy in Haskell, when scopes and inputs to functions are much more carefully controlled than a traditional language.
I use Ruby and JavaScript professionally. I do like Ruby for a number of reasons, and JavaScript I can at least live with. But in both languages, I constantly find myself spending tons of time tracking down bugs that would have been discovered right away if I had Haskell's type system at my disposal.
Attributing your coworkers' "if it builds, it works" assumptions to static typing strikes me as incorrect. Static typing, at least in Java, ensures only a (relatively) small set of conditions are true; I don't think you can really blame that for increased/additional assumptions on the part of sub-par programmers.
I've found that debugging in a dynamic environment is great for managing types in a well architected system. It falls down in a poorly architected one, in ways that static typing would not ave allowed. However, in such systems, "it compiles so it must be correct," programmers just get stuck in deeper snow.
I'm even becoming more and more fond of C++ because of the constraints that you can work in with proper use of templates; my only beef there is that the error messages when you do the Wrong Thing are often not conducive to understanding what the Right Thing is.