User9123
User9123

Reputation: 33

Haskell CIS194 Spring 13 Homework 6 Exercise 5

http://www.seas.upenn.edu/~cis194/spring13/hw/06-laziness.pdf

The question is about representing the ruler function

0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, . . . where the nth element in the stream (assuming the first element corresponds to n = 1) is the largest power of 2 which evenly divides n

I have working solution using show to display the first 20 elements with interleaveStreams' function:

data Stream a = Cons a (Stream a)

streamToList :: Stream a -> [a]
streamToList (Cons a b) = a : streamToList b 

instance Show a => Show (Stream a) where
  show = show . take 20 . streamToList

streamRepeat :: a -> Stream a
streamRepeat a = Cons a (streamRepeat a)

ruler :: Stream Integer
ruler = 
  let s n = interleaveStreams' (streamRepeat n) (s (n+1)) 
  in s 0

interleaveStreams :: Stream a -> Stream a -> Stream a
interleaveStreams (Cons x xs) (Cons y ys) = Cons x (Cons y (interleaveStreams xs ys))

interleaveStreams' :: Stream a -> Stream a -> Stream a
interleaveStreams' (Cons x xs) y = Cons x $ interleaveStreams' y xs

However I don't understand why ruler using the interleave function instead does not terminate with Show.

Upvotes: 2

Views: 245

Answers (1)

Random Dev
Random Dev

Reputation: 52280

first congrats - you solved the hard part ;)

To your question: if you use the interleaveStreams than it will pattern match on the second Cons too - but if you look at your code you see that the second part is generated by:

let s n = interleaveStreams ... (s (n+1)) 

so if interleaveStreams now asks it to produce a Cons for this part too you end up in an infinite loop

The other function solves this issue by only forcing the first constructor, which you get at once fromstreamRepeat

interleaveStreams:

s 0
= interleaveStreams (streamRepeat 0) (s 1))
{ need both cons }
= interleaveStreams (Cons 0 (streamRepeat 0)) (s 1)
= interleaveStreams (Cons ...) (interleaveStreams (streamRepeat 1) (s 2))
{ the inner interleaveStream needs both Cons again for it's pattern }
= ...

you'll never get to a Cons and streamToList will never be able to produce a list-cons and then you have your problem

interleaveStreams':

s 0
= interleaveStreams' (streamRepeat 0) (s 1))
{ need only first Cons for the pattern-match }
= interleaveStreams' (Cons 0 (streamRepeat 0)) (s 1)
= Cons 0 $ interleaveStreams' (s 1) (streamRepeat 0)
= ...

as you can see you get the Cons which is the happy path for laziness in show/streamToList


a bit shorter

by the way: you can write this without the s inner-function by using streamMap:

ruler :: Stream Integer
ruler = interleaveStreams (streamRepeat 0) (streamMap (+1) ruler)

Upvotes: 2

Related Questions