Static typing à la C,C++,Java,js,etc... is from my experience inferior to dynamic typing.
On the other hand, the Haskell typing is superior in many ways to dynamic typing. Because you lose very few power of expression but you gain a lot of time in your workflow (and also security).
Concrete example (I'll use js):
function showField(data) {
console.log(data.field1.filed2);
}
you relaunch your application, you click on three to four elements, then you enter your name and a password. You click on the button, and "BAM, filed2 doesn't exists".
Correct the typo or add a test and replay the game of executing your new code.
Static typing:
showField : ConcreteData -> IO ()
showField data = putStrLn $ filed2 (field1 data)
Try to compile, get the error:
showField : filed2 doesn't exists, may be you mean "field2".
correct your code. Now you're done.
Here is another example:
Dynamic typing:
function showField(data) {
if (data.field) { console.log("OK"); }
else { console.log("Not OK")}
}
...
showField(myData);
Try it using all manipulations to reach the test case, then:
ERROR, couldn't find field for null. :-|
replace by if (data && data.field).
In static typing:
showField data = if (field data)
then putStrLn "OK"
else putStrLn "Not OK"
compile: could not match String with (Maybe String) at ...
now:
showField data
| field data == Nothing = putStrLn "Not OK"
| otherwise = putStrLn "OK"
You fixed it, but you didn't had to lose your time searching
the error.
The BIG bonus is that you detect the error before you discover it at runtime. Imagine you didn't detected the error during your test (even with unit tests). This is why it is so easy to push this kind of error in production in a dynamic typed language.
And I only scratched the surface.
I program mostly in JS now, and I love even more when I am doing Haskell.