{-# LANGUAGE Rank2Types, ExistentialQuantification #-}

module Data.Stream
  ( Stream
  , mkStream
  , head
  , tail
  , cons
  , elemAt
  , toSeq
  , toList
  , mapStreamSt
  , fibs
  ) where

import Prelude hiding (head, tail, drop)
import Control.Comonad
import Control.Arrow ((&&&))

data Stream a = forall b.  S b (b -> a) (b -> b)

mkStream :: b -> (b -> a) -> (b -> b) -> Stream a
mkStream x f g = S x f g

instance Functor Stream where
  fmap = liftW

instance Comonad Stream where
  extract (S s f _)  = f s
  extend h (S s f g) = S s (\s' -> h (S s' f g)) g

head :: Stream a -> a
head (S x f _) = f x

tail :: Stream a -> Stream a
tail (S x f g) = S (g x) f g

cons :: a -> Stream a -> Stream a
cons x s = mkStream (x,s) fst ((head &&& tail) . snd)

elemAt :: Integral i => i -> Stream a -> a
elemAt n = head . drop n

drop :: Integral i => i -> Stream a -> Stream a
drop 0 = id
drop n = drop (n - 1) . tail

toSeq :: Stream a -> Int -> a
toSeq = flip elemAt

toList :: Stream a -> [a]
toList = map head . iterate tail

mapStreamSt :: (a -> s -> b) -> (a -> s -> s) -> s -> Stream a -> Stream b
mapStreamSt f1 f2 s0 xs
  = mkStream (xs,s0) 
             (\(x,s) -> f1 (head x) s)
             (\(x,s) -> (tail x, f2 (head x) s))

fibs :: Stream Integer
fibs = mkStream (1,1) fst (\(i,j) -> (j,i+j))

{-
------
-- A stream can be pulled from any comonad

parallelW :: Comonad w => w (Stream a) -> Stream (w a)
parallelW w = mkStream w (fmap head) (fmap tail)

-- in fact, from any functor
parallelF :: (Functor f) => f (Stream a) -> Stream (f a)
parallelF f = mkStream f (fmap head) (fmap tail)


------
-- What good are these?

mapW :: Comonad w => (w a -> b) -> w (Stream a) -> Stream b
mapW f = fmap f . parallelW

unfold :: (b -> (a,b)) -> b -> Stream a
unfold f i = mkStream i (fst . f) (snd . f)

unfoldW :: Comonad w => (w b -> (a,b)) -> w b -> Stream a
unfoldW f w = mkStream w (fst . f) (=>> snd . f)

mkStreamW :: Comonad w => w b -> (w b -> a) -> (w b -> b) -> Stream a
mkStreamW w f g = mkStream w f (=>> g)
-}