According to the Haskell 2010 report, init is defined as the following:
init :: [a] -> [a]
init [x] = []
init (x:xs) = x : init xs
init [] = error "Prelude.init: empty list"
base-4.4.1.0 defines it similarly. To me, it seems that it would be perfectly acceptable and intuitive to have:
init [] = []
which would make init a total function. Since this definition made it into the haskell 2010 report I guess that there are arguments for it. Is that the case or is it defined that way because of tradition and backwards compatibility?
The same reason
tail []isn’t[]; it breaks the invariants that define these functions. We can defineheadandtailby saying that ifheadandtailare defined for a valuexs, thenxs ≡ head xs : tail xs.As Niklas pointed out, the invariant we want to define
initandlastisxs ≡ init xs ++ [last xs]. It’s true thatlastisn’t defined on empty lists either, but why shouldlastbe defined ifinitcan’t be? Just like it would be wrong if one ofheadortailwas defined on an input while the other wasn’t,initandlastare two sides of the same coin, splitting a list into two values that together are equivalent to the original.For a more “practical” view (although being able to reason about programs in terms of useful invariants about the operations they use has very practical benefit!), an algorithm that uses
initwill probably not behave correctly on an empty list, and ifinitworked that way, it would work to hide the errors produced. It’s better forinitto be conservative about its inputs so that consideration has to be given for edge-cases like empty list when it is used, especially when it isn’t at all clear that the proposed value for it to return in those cases is reasonable.Here’s a previous Stack Overflow question about
headandtail, including whytail []doesn’t just return[]; the answers there should help to explain why these invariants are so important.