This is naïve, IMHO.
Using a statically typed language with a powerful type system means that you can use the compiler more often to turn run-time assertions into compile-time assertions. In that sense, the compiler is like a proof-assistant that can automatically verify certain properties about your program.
However, I have yet to see a language in which you can actually prove everything of interest to the compiler. Swift, with its lack of HKT and no support for dependent types, certainly can't prove every interesting property at compile time yet (e.g. about the length of an array). Therefore, the need for runtime assertions arises.
Runtime assertions enable you to say "I strongly believe that this condition holds, however I can't formally verify it (via the compiler). If this condition should, contrary to my belief, not hold anymore, or if surrounding code ever changes in such a way that the condition is invalidated, I want this to fail as fast as possible, so I am immediately made aware of the problem, instead of passing on nonsensical values and getting wrong results in the end, long after the initial error actually occured".
This in fact, is the same situation where you would use unchecked exceptions in Java or RuntimeErrors in Ruby: for unrecoverable errors, where the only thing you can do is throw your hands up in the air and say "well, this is bad, we should fix it".
Think about what the alternative would be: You have a situation where you know that your array must be non-empty. Certainly, if it were empty for some reason that would indicate a bug that should be fixed. Now image you want to call first on your array - what kind of options do you have now?
You could:
- pass on the optional value. At some point, you'll have to unwrap it though, so you're only deferring the problem to a point where, when the condition is violated, it's very hard to pin-point what went wrong.
- use force unwrap. But that also goes against your "avoid crashing" philosophy, and it also gives you worse diagnostics than the proposal here
- use guard/let. But then, what's your alternative? You know the empty array / whatever other case can never (or at least, should never) occur, so what kind of alternative implementation would you provide? Some random default value? How does that help?
- throw an error and have some kind of error handling at the outermost level. this works, but unfortunately forces you to annotate every intermediate function with throws, which for conditions you know to be wrong, seems like an overkill. Worse, this is the same kind of "checked exceptions" fiasco that went horribly wrong in Java (IMHO, not because checked exceptions are per se wrong, but because they were overused for unrecoverable errors and therefore caused lots of pain in client code. Ask any Java programmer how many empty catch blocks he has seen in enterprise code.)
- or you actually use guard/let but with a fatalError. But this is exactly what this proposal wants to have a shorthand for.
In practical terms, what you want - avoid crashing - doesn't even work. Every time you do an array access you're risking a crash (array access returning optionals was ruled out for performance reasons, afaik). Every time you add or multiply integers, you risk a crash, unless you use the special functions that check for overflow (but these are very cumbersome, so they're not often used). So in any program of sufficient complexity, crashes will occur. The question is how to deal with them, and crashes with good diagnostics are much more helpful than "SIGILL".
Now there is a problem, unfortunately, in that Swift doesn't allow you to recover from crashes (unlike every interpreted language, where every error is recoverable, because the runtime can deal with it, including JVM languages). This, IMHO, is a problem that should be solved at some other level. In an ideal future, I would love to have a concept similar to Erlang in which you can have supervisor trees where parts of the program can restart other parts of the program... but this is probably a pipe dream for now.
In sever-side land, where I am, currently the only solution I see is to deal with this on a container/load balancer basis, where crashed containers can be respawned and requests rerouted etc.
All that said, the motivation for the proposal makes 100% sense for me. I write guard/let + fatalError a lot, and having a shorthand for this would be a) incredibly convenient and b) encourage other (more junior) developers to actually use it to assert certain conditions instead of either using the bang notation or including some nonsensical default value in the "else" case (that is often just drawn out of thin air).
I don't have an opinion on whether it needs to be this explicit proposal, or whether some alternative with ?? and a Never type would be preferrable, but I do believe that there is a gap here that should be filled somehow.