
On Wed, Oct 21, 2009 at 9:44 PM, Wouter Swierstra
The only change with the previous version has been to add irrefutable patterns to several function definitions. This is rather delicate design decision: too many irrefutable patterns could result in thunks not being evaluated; too few irrefutable patterns could cause your functions diverge. As a rule of thumb I've chosen only to use irrefutable patterns in functions that produce streams from streams. The operations that observe finite information (a prefix, the element at index i, etc.) do not have have irrefutable patterns and force evaluation to weak head normal form.
Hi Wouter, I have two questions: 1) What's the difference between your: "tail ~(Cons _ xs) = xs" and the more simple: "tailStrict (Cons _ xs) = xs" ? I know they're desugared to: "tail ys = let Cons _ xs = ys in xs" and: "tailStrict ys = case ys of Cons _ xs -> xs" respectively. But aren't they operationally the same: "tail undefined = undefined" and: "tailStrict undefined = undefined" 2) Why don't you also use an irrefutable pattern in "take"? "take" is now defined as: take :: Int -> Stream a -> [a] take n (Cons x xs) | n == 0 = [] | n > 0 = x : (take (n - 1) xs) | otherwise = error "Stream.take: negative argument." so "take 0 undefined = undefined" while: takeLazy :: Int -> Stream a -> [a] takeLazy n ~(Cons x xs) | n == 0 = [] | n > 0 = x : (takeLazy (n - 1) xs) | otherwise = error "Stream.take: negative argument." "takeLazy 0 undefined = []" regards, Bas