
I'm happy to announce a new release of the Data.Stream library. http://hackage.haskell.org/package/Stream 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. I've uploaded a new version to Hackage. I'd be interested to hear if any existing code takes a performance hit as a result of these changes. Wouter

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

Bas van Dijk wrote:
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"
I concur, a strict tail is enough. Writing foo xs = bar (tail xs) has the same effect as foo xs = bar (tailStrict xs) since the evaluation of xs is deferred in both cases. Regards, apfelmus -- http://apfelmus.nfshost.com

1) What's the difference between your: "tail ~(Cons _ xs) = xs" and the more simple: "tailStrict (Cons _ xs) = xs" ?
I'm no expert - but I can't think of any difference at all.
2) Why don't you also use an irrefutable pattern in "take"? "take" is now defined as:
This is a trickier question: should "take 0 undefined" by [] or undefined? I'm not sure what the best choice is. I suppose it makes sense to stick with the behaviour of Data.List and return an empty list, even if any program that relies on this not being undefined is probably broken. I've uploaded a new version. Thanks for your comments! Wouter
participants (4)
-
Bas van Dijk
-
Heinrich Apfelmus
-
Wouter Swierstra
-
Wouter Swierstra