[Haskell-cafe] Is "take" behaving correctly?
westondan at imageworks.com
Wed Sep 12 15:30:17 EDT 2007
One idiom I rely on pretty often is
> (id &&& tail) >>> uncurry (zipWith f)
to do pairwise binary operations (though I suspect an Applicative
functor might be a better way to go?).
E.g. my solution for ProjectEuler problem #18 (max sum of vertical path
through a triangle of integers) is:
> f = curry $ second ((id &&& tail) >>>
> uncurry (zipWith max)) >>>
> uncurry (zipWith (+) )
> zeroes = repeat 0
> maxTotal = foldr f zeroes
Key to this idiom is that zipWith stops after the shortest element.
Otherwise, I'd need an extra init function and have to test for empty lists.
So there is at least one good reason for the way zipWith works.
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.
> There are two sides to this form of "grace", though. I'll grant you, it's
> quite hard to pull off a successful fraud without a degree of aplomb.
> 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.
> 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. Problem:
> unless you were doing fairly wacky stuff, they both happened to have length
> zero. Once weirder things arrived, the discrepancies showed up and the
> truncations started happening, causing well-formed but ill-typed
> to be generated by and propagated around the kernel of the system, which
> eventually choked in some essentially random place. We had many suspects
> before we found the culprit. If the programmer had used a version of zip
> which bombed in off-diagonal cases, we'd have been straight there.
> So I might very well consider having more than one version of take around,
> depending on my requirements for its use. I might even consider the more
> informative function which also returns the length of the shortfall.
> In a dependently typed setting, I wouldn't write take and drop: I'd equip
> lists with a view incorporating both, guaranteeing the length invariants
> and that the sublists actually append to the input. But where shame is
> unattainable, blame is really quite important.
> All the best
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
More information about the Haskell-Cafe