
Gregory Crosswhite wrote:
Yes, but I think that it is also important to distinguish between cases where an error is expected to be able to occur at runtime, and cases where an error could only occur at runtime *if the programmer screwed up*.
Well sure, but how can you demonstrate that you (the programmer) haven't screwed up? That's the point that I'm most interested in. Certainly there is a difference between exceptional behavior at runtime (e.g., due to malformed inputs) vs programming errors. We can't stop the former without broadening the scope of the typesystem to include runtime inputs; but we can, at least idealistically, stop the latter. While complex invariants require full dependent types to capture, there are a surprising number of invariants that Haskell's type system can already capture (even without GADTs!).
If you structure your code to preserve and rely on the invariant that a given list has at least two elements, then it makes sense to call secondElement because if the list doesn't have two elements then you screwed up. Furthermore, there is no way to recover from such an error because you don't necessarily know where the invariant was broken because if you did you would have expected the possibility and already fixed it.
If you're structuring your code with that invariant, then why aren't you using the correct type? data AtLeastTwo a = ALT a a [a] If your response is that you use too many of the standard list functions, then write a module with ALT versions (because evidently you need one). If your response is that you convert between lists and ALT too often, then you probably need to restructure the code. At the very least you should be using a newtype and smart constructors, so that you can actually prove your invariant whenever necessary: newtype AtLeastTwo a = Hidden_ALT [a] altEnd :: a -> a -> AtLeastTwo a altCons :: a -> AtLeastTwo a -> AtLeastTwo a altList :: a -> a -> [a] -> AtLeastTwo a fromList :: [a] -> Maybe (AtLeastTwo a) toList :: AtLeastTwo a -> [a] Without smart constructors or an ADT to enforce your invariants, why should you be convinced that you (the programmer) haven't messed up? The cheap and easy access to ADTs is one of the greatest benefits of Haskell over mainstream languages, don't fear them. -- Live well, ~wren