
Jules Bean wrote:
wren ng thornton wrote:
I have long been disappointed by a number of `error`s which shouldn't be. For example, the fact that `head` and `div` are not total strikes me as a (solvable) weakness of type checking, rather than things that should occur as programmer errors/exceptions at runtime. The use of `error` in these cases muddies the waters and leads to a laissez-faire attitude about when it's acceptable to throw one's hands up in despair and use `error` rather than writing a better function.
head uses "error" in precisely the correct, intended fashion.
head has a precondition (only call on non-empty lists)
And that is *exactly* my complaint: the precondition is not verified by the compiler. Therefore it does not exist in the semantic system, which is why the error screws up semantic analysis. The type of head should not be [a] -> a + Error, it should be (a:[a]) -> a. With the latter type the compiler can ensure the precondition will be proved before calling head, thus eliminating erroneous calls. It's a static error, detectable statically, and yet it's deferred to the runtime. I'd much rather the compiler catch my errors than needing to create an extensive debugging suite and running it after compilation. Is this not the promise of purity?
There are programming styles which avoid using 'head'. You are free to use those if you don't like it. I myself am content to use 'head' on lists which I know are guaranteed to be non-empty.
Avoiding head is not a tenable solution. The syntax for case matching is insufficiently first-class, so avoiding the function often leads to contortions, illegible code, or reinventing the problem. Moreover, this isn't an isolated case; fromJust is another canonical example, as is any other partial algebra-homomorphism. Many mathematical functions like div are also subject to trivially-verified invariants on their arguments. Functions like uncons and viewL are nicer (because they're safe), but they can have overhead because they're unnecessarily complete (e.g. the Maybe wrapper can be avoided if we know a-priori that Just will be the constructor used). -- Live well, ~wren