Stream-0.2.2: Data/Stream.hs
-- | Streams are infinite lists. Most operations on streams are
-- completely analogous to the definition in Data.List.
module Data.Stream
(
-- * The type of streams
Stream(..)
-- * Basic functions
, (<:>)
, head
, tail
, inits
, tails
-- * Stream transformations
, map
, intersperse
, interleave
, scanl
, scanl1
-- * Building streams
, iterate
, repeat
, cycle
, unfold
-- * Extracting sublists
, take
, drop
, splitAt
, takeWhile
, dropWhile
, span
, break
, filter
, partition
-- * Sublist predicates
, isPrefixOf
-- * Indexing streams
, (!!)
-- * Zipping and unzipping streams
, zip
, zipWith
, unzip
-- * Functions on streams of characters
, words
, unwords
, lines
, unlines
-- * Converting to and from an infinite list
, listToStream
, streamToList
)
where
import Prelude hiding (head, tail, map, scanl, scanl1,
iterate, take, drop, takeWhile,
dropWhile, repeat, cycle, filter, (!!), zip, unzip,
zipWith,words,unwords,lines,unlines, break, span, splitAt)
import Control.Applicative
import Data.Char (isSpace)
-- | An infinite sequence.
data Stream a = Cons a (Stream a) deriving (Show, Eq)
infixr 5 `Cons`
instance Functor Stream where
fmap f (Cons x xs) = Cons (f x) (fmap f xs)
instance Applicative Stream where
pure = repeat
(<*>) = zipWith ($)
instance Monad Stream where
return = repeat
(Cons x xs) >>= f = Cons (head (f x)) (tail (xs >>= f))
infixr 5 <:>
-- | The @ \<:\> @ operator is an infix version of the 'Cons'
-- constructor.
(<:>) :: a -> Stream a -> Stream a
(<:>) = Cons
-- | Extract the first element of the sequence.
head :: Stream a -> a
head (Cons x _ ) = x
-- | Extract the sequence following the head of the stream.
tail :: Stream a -> Stream a
tail (Cons _ xs) = xs
-- | 'intersperse' @y@ @xs@ creates an alternating stream of
-- elements from @xs@ and @y@.
intersperse :: a -> Stream a -> Stream a
intersperse y (Cons x xs) = Cons x (Cons y (intersperse y xs))
-- | Interleave two Streams @xs@ and @ys@, alternating elements
-- from each list.
--
-- > @[x1,x2,...] `interleave` [y1,y2,...] == [x1,y1,x2,y2,...]@
interleave :: Stream a -> Stream a -> Stream a
interleave (Cons x xs) ys = Cons x (interleave ys xs)
-- | Apply a function uniformly over all elements of a sequence.
map :: (a -> b) -> Stream a -> Stream b
map f (Cons x xs) = Cons (f x) (map f xs)
-- | The unfold function is similar to the unfold for lists. Note
-- there is no base case: all streams must be infinite.
unfold :: (c -> (a,c)) -> c -> Stream a
unfold f c =
let (x,d) = f c
in Cons x (unfold f d)
-- | 'iterate' @f@ @x@ function produces the infinite sequence
-- of repeated applications of @f@ to @x@.
--
-- > iterate f x = [x, f x, f (f x), ..]
iterate :: (a -> a) -> a -> Stream a
iterate f x = Cons x (iterate f (f x))
-- | 'scanl' yields a stream of successive reduced values from the
-- | left:
--
-- > scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...]
scanl :: (a -> b -> a) -> a -> Stream b -> Stream a
scanl f z (Cons x xs) = z <:> scanl f (f z x) xs
-- | 'scanl1' is a variant of 'scanl' that has no starting value argument:
--
-- > scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]
scanl1 :: (a -> a -> a) -> Stream a -> Stream a
scanl1 f (Cons x xs) = scanl f x xs
-- | 'take' @n@ @xs@ returns the first @n@ elements of @xs@.
--
-- /Beware/: passing a negative integer as the first argument will
-- cause an error.
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."
-- | 'drop' @n@ @xs@ drops the first @n@ elements off the front of
-- the sequence @xs@.
--
-- /Beware/: passing a negative integer as the first argument will
-- cause an error.
drop :: Int -> Stream a -> Stream a
drop n xs
| n == 0 = xs
| n > 0 = drop (n - 1) (tail xs)
| otherwise = error "Stream.drop: negative argument."
-- | 'takeWhile' @p@ @xs@ returns the longest prefix of the stream
-- @xs@ for which the predicate @p@ holds.
takeWhile :: (a -> Bool) -> Stream a -> [a]
takeWhile p (Cons x xs)
| p x = x : takeWhile p xs
| otherwise = []
-- | 'dropWhile' @p@ @xs@ returns the suffix remaining after
-- 'takeWhile' @p@ @xs@.
--
-- /Beware/: this function may diverge if every element of @xs@
-- satisfies @p@, e.g. @dropWhile even (repeat 0)@ will loop.
dropWhile :: (a -> Bool) -> Stream a -> Stream a
dropWhile p (Cons x xs)
| p x = dropWhile p xs
| otherwise = Cons x xs
-- | 'repeat' @x@ returns a constant stream, where all elements are
-- equal to @x@.
repeat :: a -> Stream a
repeat x = Cons x (repeat x)
-- | 'cycle' @xs@ returns the infinite repetition of @xs@:
--
-- > cycle [1,2,3] = Cons 1 (Cons 2 (Cons 3 (Cons 1 (Cons 2 ...
cycle :: [a] -> Stream a
cycle xs = foldr Cons (cycle xs) xs
-- | 'filter' @p@ @xs@, removes any elements from @xs@ that do not satisfy @p@.
--
-- /Beware/: this function may diverge if there is no element of
-- @xs@ that satisfies @p@, e.g. @filter odd (repeat 0)@ will loop.
filter :: (a -> Bool) -> Stream a -> Stream a
filter p (Cons x xs)
| p x = Cons x (filter p xs)
| otherwise = filter p xs
-- | @xs !! n@ returns the element of the stream @xs@ at index
-- @n@. Note that the head of the stream has index 0.
--
-- /Beware/: passing a negative integer as the first argument will cause
-- an error.
(!!) :: Stream a -> Int -> a
(!!) (Cons x xs) n
| n == 0 = x
| n > 0 = xs !! (n - 1)
| otherwise = error "Stream.!! negative argument"
-- | The 'zip' function takes two streams and returns a list of
-- corresponding pairs.
zip :: Stream a -> Stream b -> Stream (a,b)
zip (Cons x xs) (Cons y ys) = Cons (x,y) (zip xs ys)
-- | The 'unzip' function is the inverse of the 'zip' function.
unzip :: Stream (a,b) -> (Stream a, Stream b)
unzip (Cons (x,y) xys) = (Cons x (fst (unzip xys)),
Cons y (snd (unzip xys)))
-- | The 'zipWith' function generalizes 'zip'. Rather than tupling
-- the functions, the elements are combined using the function
-- passed as the first argument to 'zipWith'.
zipWith :: (a -> b -> c) -> Stream a -> Stream b -> Stream c
zipWith f (Cons x xs) (Cons y ys) = Cons (f x y) (zipWith f xs ys)
-- | 'span' @p@ @xs@ returns the longest prefix of @xs@ that satisfies
-- @p@, together with the remainder of the stream.
span :: (a -> Bool) -> Stream a -> ([a], Stream a)
span p (Cons x xs)
| p x = let (trues, falses) = span p xs
in (x : trues, falses)
| otherwise = ([], Cons x xs)
-- | The 'break' @p@ function is equivalent to 'span' @not . p@.
break :: (a -> Bool) -> Stream a -> ([a], Stream a)
break p = span (not . p)
-- | The 'words' function breaks a stream of characters into a
-- stream of words, which were delimited by white space.
--
-- /Beware/: if the stream of characters @xs@ does not contain white
-- space, accessing the tail of @words xs@ will loop.
words :: Stream Char -> Stream String
words xs = let (w, ys) = break isSpace xs
in Cons w (words ys)
-- | The 'unwords' function is an inverse operation to 'words'. It
-- joins words with separating spaces.
unwords :: Stream String -> Stream Char
unwords (Cons x xs) = foldr Cons (Cons ' ' (unwords xs)) x
-- | The 'lines' function breaks a stream of characters into a list
-- of strings at newline characters. The resulting strings do not
-- contain newlines.
--
-- /Beware/: if the stream of characters @xs@ does not contain
-- newline characters, accessing the tail of @lines xs@ will loop.
lines :: Stream Char -> Stream String
lines xs = let (l, ys) = break (== '\n') xs
in Cons l (lines (tail ys))
-- | The 'unlines' function is an inverse operation to 'lines'. It
-- joins lines, after appending a terminating newline to each.
unlines :: Stream String -> Stream Char
unlines (Cons x xs) = foldr Cons (Cons '\n' (unlines xs)) x
-- | The 'isPrefix' function returns @True@ if the first argument is
-- a prefix of the second.
isPrefixOf :: Eq a => [a] -> Stream a -> Bool
isPrefixOf [] _ = True
isPrefixOf (y:ys) (Cons x xs)
| y == x = isPrefixOf ys xs
| otherwise = False
-- | The 'partition' function takes a predicate @p@ and a stream
-- @xs@, and returns a pair of streams. The first stream corresponds
-- to the elements of @xs@ for which @p@ holds; the second stream
-- corresponds to the elements of @xs@ for which @p@ does not hold.
--
-- /Beware/: One of the elements of the tuple may be undefined. For
-- example, @fst (partition even (repeat 0)) == repeat 0@; on the
-- other hand @snd (partition even (repeat 0))@ is undefined.
partition :: (a -> Bool) -> Stream a -> (Stream a, Stream a)
partition p (Cons x xs) =
let (trues,falses) = partition p xs
in if p x then (Cons x trues, falses)
else (trues, Cons x falses)
-- | The 'inits' function takes a stream @xs@ and returns all the
-- finite prefixes of @xs@.
inits :: Stream a -> Stream ([a])
inits (Cons x xs) = Cons [] (fmap (x:) (inits xs))
-- | The 'tails' function takes a stream @xs@ and returns all the
-- suffixes of @xs@.
tails :: Stream a -> Stream (Stream a)
tails xs = Cons xs (tails (tail xs))
-- | The 'splitAt' function takes an integer @n@ and a stream @xs@
-- and returns a pair consisting of the prefix of @xs@ of length
-- @n@ and the remaining stream immediately following this prefix.
--
-- /Beware/: passing a negative integer as the first argument will
-- cause an error.
splitAt :: Int -> Stream a -> ([a], Stream a)
splitAt n xs
| n == 0 = ([],xs)
| n > 0 = let (prefix,rest) = splitAt (n-1) (tail xs)
in (head xs : prefix, rest)
| otherwise = error "Stream.splitAt negative argument."
-- | The 'streamToList' converts a stream into an infinite list.
streamToList :: Stream a -> [a]
streamToList (Cons x xs) = x : streamToList xs
-- | The 'listToStream' converts an infinite list to a
-- stream.
--
-- /Beware/: Passing a finite list, will cause an error.
listToStream :: [a] -> Stream a
listToStream (x:xs) = Cons x (listToStream xs)
listToStream [] = error "Stream.listToStream applied to finite list"