The former is when you want the program to do X, and X itself is wrong. Sure, defending against this is rarely possible. The latter is in my experience far more common. When you want the program to do X, but you actually told it to do Y.
Haskell catches most of the latter cases.
One of the main reasons is the functional purity. Because return values are the only result of a function, and these values are type-checked against a specification, it is much harder to do the wrong thing.
For example, an imperative program will idiomatically have methods whose sole effect is mutating their arguments or the objects. Even with a type system -- if you just omit the effects, the compiler really has no way to know that you meant the effects to happen, and you will simply have a bug. A purely functional program will idiomatically use a different style. Instead of mutating arguments and objects, it will return a new value. If you forget to return a new value, the compiler will catch that.
Additionally, the types in Haskell are generic by default. The more generic the type of a function, the more restricted is what that function can do. The restrictions narrow the space of wrong things you can do.
Another interesting property of Haskell is where it chooses to be on the "restrictiveness+guarantess vs. powerful+unguaranteed" trade-off.
It seems many don't even notice that the other side of the "power" coin is "guarantees". The more powerful a construct is -- the less you can say about what it will do. In Haskell, the purity and generic types of functions are two things that heavily restrict code. But there are many other mechanisms to restrict rather than empower code. In many contexts, restrictiveness is considered a bad thing, but in Haskell, this restrictiveness is very useful for reasoning about code.
For example, the function of type: (forall a. a -> a) in Haskell can only be the identity function. It cannot print "hello world", and it cannot mutate variables. These restrictions are useful because we now can have useful laws. For example: id . f = f . id = f. And we can derive these useful laws from the type itself, without even looking at the code!
By helping us find the most restrictive sub-language that can express the solution, we rule out even more bugs.
It is on the Pareto frontier of the two. Which verges on tautological, but it's still a useful observation. If you can trivially extend the power of a construct while maintaining the same guarantees, the construct was simply broken, and extending the guarantees of a construct without affecting its power means simply that the guarantees were excessively conservative in the first place.
(This can almost be copied and pasted any time anybody claims two properties of a program aren't in conflict, the most common probably being security and ease of programming.)
We may be wrong about where the frontier is... but I doubt we've very wrong.
So using an untyped language only to run an analysis on it later doesn't really seem all that appealing to me, even if it could somehow provide the guarantees of Haskell or dependently typed languages.
Some of them may class as "design" rather than "coding" bugs but they still aren't usually what the programmer intended, and I would say that the majority of production defects that I see in our systems these days would fall into this category.
1. You think about an algorithm
2. You (try to) code it
3. A first time to discover an error: compilation and/or syntax verification.
4. Then you test your code
Depending of the task and the fail, if 3 or 4 fail you go to 2 or 1.With a standard language (C/C++/Java/Python/Ruby/Javascript...) The probability of 3 to fail is very small when you are used to your language. But the probability to fail at step 4 is High.
In haskell this is exactly the opposite, with many advantages:
Compiler error in Haskell gives you a meaningful and informative message. Most of the time, it even gives you a hint on how to solve the problem.
In the end, most of the time, once the compilation phase passed, my program worked as I expected.
Furthermore, with Haskell you can use quickcheck, which most of the time will make better test than yours. I know there is some quickcheck equivalent in other languages but I believe this is not standard.
In code where the types start to to tell you less, e.g. when doing a bunch of number crunching, and you have a signature that looks like:
func :: Int -> Int -> Int -> Int
It's common for the programmer to use a "newtype wrapper" to bring some of the logic into the type system like: newtype Velocity = Velocity Int
newtype Force = Force Int
newtype Iteration = Iteration Int
Then the type system will catch if you accidentally use the Velocity when what you want is Force, etc.http://necrobious.blogspot.com/2009/03/fun-example-of-haskel...
http://flygdynamikern.blogspot.com/2011/04/announce-dimensio...
http://book.realworldhaskell.org/read/data-structures.html#x...
The problems you might run into are more (for example) using the wrong algorithm for calculating gravity, adjusting the position of one planet before using it to evaluate its impacts on other planets, failing in collision detection etc.
I don't see anything in Haskell that would prevent me from doing any of those.
That doesn't help at all, are you serious? Cause you genuinely sound like you are trolling. Especially when you say things like "if a language can't prevent every possible bug then there's no poing having it prevent any bugs".
"If your program compile it will be very close to what the programmer intended".
There's nothing I've seen in Haskell, or any other language, that will prevent you implementing the wrong algorithm, or the wrong business rules etc. If you're doing a phsyics simulator (to take an example I've played around with), Haskell is not going to stop you badly miscalculating the gravitational effect of multiple objects, or calculating the mass of an object incorrectly.