> No exceptions
>
> Exceptions are also a side effect handled by the type
> system. We know our function is pure and won’t throw
> an exception.
This isn't the case for Haskell.
> The input can either be true or false. Once you have
> tested the result of these two possibilities, you
> have the holy grail. No exceptions, no infinite
> loops, no incorrect results, no errors.
Almost. The input can also be bottom (a thunk that, when forced, loops infinitely or raises an exception). In that case, if the function tries to inspect the input then the function will also be a thunk that, when forced, loops infinitely or throws an exception. If the function does not inspect its input, it may still return a value (e.g. `const True`).
Yeah, many (myself included) share the view that it's a wart in the Haskell standard lib. There are good alternatives to using that function, though (same for most of the other unfortunately partial functions).
There are libraries that reimplement these types of functions using Maybes for the reason you would guess. I think it's mostly a historical artifact at this point.
If you look at the type of `error`, `assert`, or `undefined` (or `throw`, also defined in Control.Exception) you'll see that they actually don't show up in the type:
Prelude> :t error
error :: [Char] -> a
Prelude> :t undefined
undefined :: t
Prelude> :t assert
assert :: Bool -> a -> a
Prelude Control.Exception> :t throw
throw :: Exception e => e -> a
This means you can throw from anywhere. You can only catch in IO.
In the case of error, assert and undefined, I wouldn't class them as exceptions but as unrecoverable errors, as discussed in the linked wiki page.
I wasn't aware of 'throw' and that certainly weakens my argument. You could maybe argue it is an escape hatch in the same vein as unsafePerformIO, but I don't know how it is viewed by the community.
You could argue I'm playing word games here, and I have some sympathy for that argument. What I wanted to get across is checked exceptions ala. Java (although they are too commonly bypassed in that example). Maybe moving it to the documentation section is a good compromise.
> In the case of error, assert and undefined, I wouldn't class them as exceptions but as unrecoverable errors, as discussed in the linked wiki page.
Okay, but in the case of GHC you actually can recover from them.
> I wasn't aware of 'throw' and that certainly weakens my argument. You could maybe argue it is an escape hatch in the same vein as unsafePerformIO, but I don't know how it is viewed by the community.
Asynchronous exceptions are pretty widely accepted as the way to interrupt a long-running pure computation. `error` is pretty widely used on identifying programming errors and pretty well reserved to that case. There is some dispute over whether exceptions or Either is more appropriate to dealing with exceptional conditions in IO; more reluctance around exceptions for reporting exceptional conditions outside of IO (partly because of the "only catch in IO" restriction, and substantially because we have other, good methods).
> What I wanted to get across is checked exceptions ala. Java (although they are too commonly bypassed in that example).
I'd like to see some language flesh out checked exceptions with parametric polymorphism. Alack, this is not what Haskell has (yet?) done.
Note that I don't think there's really a better story out there for async exceptions - which are necessarily able to interrupt (almost) any bit of your computation. Ideally we may be able to make assertions about stack or heap overflow (especially for a non-Turing complete fragment of the language), but I'm not sure whether Idris is able to provide that. "ThreadKilled" and "UserInterrupt" are inherently unrelated to local program flow.
For synchronous exceptions Haskell is sort of middle-of-the-road, differing from other languages mostly in providing other options that are substantially better for many use cases.