In order to understand this statement, we first have to understand what a static type system buys us. In essence, what a static type system gives us, is a guarantee: iff the program type checks, a certain class of runtime behaviors cannot occur.
That sounds ominous. Well, a type checker is similar to a theorem checker. (Actually, per the Curry-Howard-Isomorphism, they are the same thing.) One thing that is very peculiar about theorems is that when you prove a theorem, you prove exactly what the theorem says, no more. (That's for example, why, when someone says "I have proven this program correct", you should always ask "please define 'correct'".) The same is true for type systems. When we say "a program is type-safe", what we mean is not that no possible error can occur. We can only say that the errors the type system promises us to prevent can't occur.
So, programs can have infinitely many different runtime behaviors. Of those, infinitely many ones are useful, but also infinitely many ones are "incorrect" (for various definitions of "correctness"). A static type system allows us to prove that a certain finite, fixed set of those infinitely many incorrect runtime behaviors cannot occur.
The difference between different type systems is basically in which, how many, and how complex runtime behaviors they can prove to not occur. Weak type systems such as Java's can only prove very basic things. For example, Java can prove that a method that is typed as returning a String
cannot return a List
. But, for example, it can not prove that the method won't not return. It can also not prove that the method won't throw an exception. And it cannot prove that it won't return the wrong String
– any String
will satisfy the type checker. (And, of course, even null
will satisfy it as well.) There are even very simple things that Java cannot prove, which is why we have exceptions such as ArrayStoreException
, ClassCastException
, or everybody's favorite, the NullPointerException
.
More powerful type systems like Agda's can also prove things like "will return the sum of the two arguments" or "returns the sorted version of the list passed as an argument".
Now, what the designers of Elm mean by the statement that they have no runtime exceptions is that Elm's type system can prove the absence of (a significant portion of) runtime behaviors that in other languages can not be proven to not occur and thus might lead to erroneous behavior at runtime (which in the best case means an exception, in a worse case means a crash, and in the worst case of all means no crash, no exception, and just a silently wrong result).
So, they are not saying "we don't implement exceptions". They are saying "things that would be runtime exceptions in typical languages that typical programmers coming to Elm would have experience with, are caught by the type system". Of course, someone coming from Idris, Agda, Guru, Epigram, Isabelle/HOL, Coq, or similar languages will see Elm as pretty weak in comparison. The statement is more aimed at typical Java, C♯, C++, Objective-C, PHP, ECMAScript, Python, Ruby, Perl, … programmers.