[Haskell-cafe] Is "take" behaving correctly?

Henning Thielemann lemming at henning-thielemann.de
Wed Sep 12 08:39:32 EDT 2007

On Wed, 12 Sep 2007, Conor McBride wrote:

> Hi folks
> On 12 Sep 2007, at 00:38, Brent Yorgey wrote:
>> On 9/11/07, PR Stanley <prstanley at ntlworld.com> wrote: Hi
>> take 1000 [1..3] still yields [1,2,3]
>> I thought it was supposed to return an error.
> [..]
>> If for some reason you want a version that does return an error in that 
>> situation, you could do something like the following:
>> take' n _ | (n <= 0) = []
>> take' n [] | (n > 0) = error "take': list too short"
>>           | otherwise = []
>> take' n (x:xs) = x : take' (n-1) xs
>> I'm not sure why you'd want that, though.  The standard implementation 
>> gracefully handles all inputs, and usually turns out to be what you want.
> I always hope that key invariants and hygiene conditions can be built into
> the static semantics of programs, but where that's impractical somehow, I
> prefer if the dynamic behaviour makes it as easy as possible to assign the
> blame for errors. In such circumstances, I'd like operations to complain
> about bogus input, rather than producing bogus output.

Seconded. The List functions have some kind of "scripting language 
semantics" or "C semantics", that is, consume almost every input, 
regardless of the meaning of the output, just in order to avoid error 
messages. Indeed there are some usages like taking a prefix of an infinite 
list (zip "abc" [1..]), where the current behaviour of 'zip' is useful. 
However it would be better to have versions of 'zip' to support these 
special cases. Ideally the equal length could be expressed in types, like

  zipInfInf :: InfiniteList a -> InfiniteList b -> InfiniteList (a,b)
  zipInf    :: InfiniteList a -> FiniteList n b -> FiniteList n (a,b)
  zip       :: FiniteList n a -> FiniteList n b -> FiniteList n (a,b)  ,

which would need dependent types.

> These GIGO problems do bite in real life. I spent a long time finding a bug
> in somebody else's typechecker which boiled down to the silly mistake of
> zipping the wrong lists together. The right lists were guaranteed to match
> in length, but there was no reason for the wrong lists to do so.

I had a bug which was due to the wrong order of applications of 'filter' 
and 'zip'. I had two lists 'a' and 'b' of the same length, where 'b' 
contained a condition for filtering, then I wrote
   zip a (filter p b)
  instead of
   filter (p . snd) $ zip a b
  The wrong version was temptingly short, but it matched the wrong 
elements. The bug had been catched early, if 'zip' would have checked the 
length of its inputs.

More information about the Haskell-Cafe mailing list