
"Neil Mitchell"
I wrote:
H'm. I've never been completely convinced that head should be in the language at all. If you use it, you really have to think, every time you type in the letters h-e-a-d, "Am I /really/ /really/ sure the argument will never be []?" Otherwise you should be using a case expression or (more often, I think) a subsidiary function with a pattern match.
I disagree (again...) - head is useful.
Given that a programming language remain computationally complete, utility is never a sufficient reason for including something in it. Guns are occasionally useful, but if you walk around with a loaded automatic down the front of your pants, you are *far* more likely to blow your own balls off than encounter circumstances where the gun would be useful. A large part of good programming language design is about preventing you from blowing your balls off (or anyone else's).
And as you go on to say, if you apply it to the infinite list, who cares if you used head. Head is only unsafe on an empty list. So the problem then becomes can you detect unsafe calls to head and let the programmer know.
No, that's the wrong approach because it relies on detecting something that (a) the programmer probably knows already and (b) is undecidable in general. It would be far better for the programmer to express this knowledge in the programme. In Haskell it's not possible for the two datatypes data NonFiniteList t = (:) {head::t, tail:: NonFiniteList t} and data List t = (:) {head::t, tail:: List t} | [] to coexist, and though it would even then have been possible to use a type system where all the operations on List were also applicable to NonFiniteList, the means available then would have lost type inference. So the consensus at the time was that doing clever stuff with types (making one a transparent supertype of the other) was too problematic, and keeping them separate would have meant providing all the list operations twice. Once typeclasses came along, this argument no longer held, but redoing lists and nonfinite lists as special cases of a sequence class would have been hard work. I think it's hard work that should have been done, though. As I said in the post to which you replied, if you use head, you really have to have a convincing argument that it isn't going to be applied to []. If you don't have one, use something other than head (it's always possible). If your programme crashes with a "head []", the only reasonable thing to do is to go through all 60 uses and check them. If you don't have time for that right now, doing an automatic replace is a reasonable interim measure, so long as you don't replace them back once you've found the particular bug. 1 i import Control.Exception . s/head/(\l -> assert (not $ null l) (head l))/g ... Ugly, and *** Exception: /tmp/foo.hs:4:7-12: Assertion failed is really ugly, but not as ugly as *** Exception: Prelude.head: empty list :-) And the ugliness might encourage a rethink of particular uses of head next time you have to look at that bit of code, and that's a good thing even if all you end up doing is manually substituting a let for the lambda and adding a comment on the lines "I really can't see how this list could ever be empty, but I can't prove it". -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk