capped-list-1.2: Data/CappedList.hs
-----------------------------------------------------------------------------
-- |
-- Module : Data.CappedList
-- Copyright : (c) 2010 John Millikin
-- License : BSD3
--
-- Maintainer : jmillikin@gmail.com
-- Portability : portable
--
-- A list-like type for lazy sequences, with a user-defined termination value.
--
-- This module uses common names and so is designed to be imported qualified:
--
-- > import qualified Data.CappedList as CL
--
-----------------------------------------------------------------------------
{-# LANGUAGE DeriveDataTypeable #-}
module Data.CappedList
( CappedList (..)
, toList
, toList_
, fromList
, null
, appendR
, appendL
, appendR_
, appendL_
, appendWith
, map
, mapEither
, concatMapM
, foldr
, foldl
, unfoldr
, length
) where
import Prelude hiding (null, map, foldl, foldr, length)
import qualified Prelude as Prelude
import Data.Monoid (Monoid, mempty, mappend)
import Control.Monad (MonadPlus, mzero, mplus, ap)
import Control.Monad.Fix (MonadFix, fix, mfix)
import qualified Control.Applicative as A
import Control.Applicative ((<$>), (<*>))
import qualified Data.Foldable as F
import qualified Data.Traversable as T
import Data.Data (Data)
import Data.Typeable (Typeable)
-- | A list-like type for lazy sequences, with a user-defined termination value.
--
data CappedList cap a
= Next a (CappedList cap a)
| Cap cap
deriving (Eq, Show, Data, Typeable)
instance Functor (CappedList cap) where
fmap = map
instance Monoid cap => Monoid (CappedList cap a) where
mempty = Cap mempty
mappend = appendWith mappend
instance Monoid cap => Monad (CappedList cap) where
(>>=) = flip concatMapM
return = flip Next (Cap mempty)
instance Monoid cap => MonadPlus (CappedList cap) where
mzero = mempty
mplus = mappend
instance Monoid cap => A.Applicative (CappedList cap) where
pure = return
(<*>) = ap
instance Monoid cap => A.Alternative (CappedList cap) where
empty = mempty
(<|>) = mappend
instance Monoid cap => F.Foldable (CappedList cap) where
foldMap = T.foldMapDefault
instance Monoid cap => T.Traversable (CappedList cap) where
sequenceA (Cap x) = A.pure $ Cap x
sequenceA (Next f fs) = Next <$> f <*> T.sequenceA fs
instance Monoid cap => MonadFix (CappedList cap) where
mfix f = case fix (f . head') of
Cap x -> Cap x
(Next x _) -> mappend (return x) (mfix (tail' . f))
-- | Convert a capped list to a standard list.
--
-- The cap is returned in the first value of the result tuple.
--
toList :: CappedList cap a -> (cap, [a])
toList = foldl (\(cap, xs) x -> (cap, x:xs)) (\cap -> (cap, []))
-- | Convert a capped list to a standard list, discarding the cap.
--
toList_ :: CappedList cap a -> [a]
toList_ = foldr (:) (const [])
-- | Convert a standard list and cap to a capped list.
--
fromList :: [a] -> cap -> CappedList cap a
fromList [] = Cap
fromList (x:xs) = Next x . fromList xs
-- | Like the standard 'Prelude.null' function.
--
null :: CappedList cap a -> Bool
null (Next _ _) = False
null _ = True
-- | Like the standard '++' function.
--
appendL :: CappedList cap1 a -> CappedList cap2 a -> (cap2, CappedList cap1 a)
appendL xs (Cap y) = (y, xs)
appendL x@(Cap _) (Next y ys) = let (cap, z) = appendL x ys in (cap, Next y z)
appendL (Next x xs) ys = let (cap, z) = appendL xs ys in (cap, Next x z)
-- | Like the standard '++' function.
--
appendR :: CappedList cap1 a -> CappedList cap2 a -> (cap1, CappedList cap2 a)
appendR (Next x xs) ys = let (cap, z) = appendR xs ys in (cap, Next x z)
appendR (Cap x) ys = (x, ys)
-- | Like the standard '++' function.
--
-- The second list's \"cap\" will be discarded; to preserve the cap, use
-- 'appendL'.
--
appendL_ :: CappedList cap1 a -> CappedList cap2 a -> CappedList cap1 a
appendL_ xs (Cap _) = xs
appendL_ (Next x xs) ys = Next x (appendL_ xs ys)
appendL_ xs (Next y ys) = Next y (appendL_ xs ys)
-- | Like the standard '++' function.
--
-- The first list's \"cap\" will be discarded; to preserve the cap, use
-- 'appendR'.
--
appendR_ :: CappedList cap1 a -> CappedList cap2 a -> CappedList cap2 a
appendR_ (Next x xs) = Next x . appendR_ xs
appendR_ _ = id
-- | Append two capped lists, merging the caps together using a user-provided
-- function.
--
appendWith :: (c -> d -> e) -> CappedList c a -> CappedList d a
-> CappedList e a
appendWith f (Cap x) (Cap y) = Cap (f x y)
appendWith f (Next x xs) ys = Next x (appendWith f xs ys)
appendWith f xs (Next y ys) = Next y (appendWith f xs ys)
-- | Like the standard 'Prelude.map' function.
--
map :: (a -> b) -> CappedList cap a -> CappedList cap b
map f = foldr (Next . f) Cap
{-# INLINE [1] map #-}
{-# RULES
"map/map" forall f g t.
map f (map g t) = map (f . g) t
#-}
-- | Like the standard 'Prelude.map' function, but the mapping function may
-- return a capping value.
--
mapEither :: (a -> Either cap b) -> CappedList cap a -> CappedList cap b
mapEither f = foldr (\a acc -> either Cap (flip Next acc) (f a)) Cap
{-# INLINE [1] mapEither #-}
{-# RULES
"mapEither/mapEither" forall f g t.
mapEither f (mapEither g t) = mapEither (either Left g . f) t
#-}
-- | Like the standard 'Prelude.concatMap' function.
--
concatMapM :: Monoid cap => (a -> CappedList cap b) -> CappedList cap a
-> CappedList cap b
concatMapM f = foldr (mappend . f) Cap
-- | Like the standard 'Prelude.foldr' function, but accepting an extra
-- parameter to handle 'Cap' values.
--
foldr :: (a -> b -> b) -> (cap -> b) -> CappedList cap a -> b
foldr f z = foldr' where
foldr' (Cap x) = z x
foldr' (Next x xs) = f x (foldr' xs)
-- | Like the standard 'Prelude.foldl' function, but accepting an extra
-- parameter to handle 'Cap' values.
--
foldl :: (b -> a -> b) -> (cap -> b) -> CappedList cap a -> b
foldl f = foldl' where
foldl' z (Cap x) = z x
foldl' z (Next x xs) = foldl' (\cap -> f (z cap) x) xs
-- | Like the standard 'Data.List.unfoldr' function, but the step function
-- must return a cap to terminate the unfold.
--
unfoldr :: (b -> Either cap (a, b)) -> b -> CappedList cap a
unfoldr f = unfoldr' where
unfoldr' x = case f x of
Left cap -> Cap cap
Right (a, b) -> Next a (unfoldr' b)
-- | Like the standard 'Prelude.length' function; 'Cap' is considered
-- 0-length.
--
length :: CappedList cap a -> Int
length = foldr (const (1 +)) (const 0)
-- Partial functions, used only to implement mfix; not exported
head' :: CappedList cap a -> a
head' (Cap _) = error "mfix CappedList: Cap"
head' (Next x _) = x
tail' :: CappedList cap a -> CappedList cap a
tail' (Cap _) = error "mfix CappedList: Cap"
tail' (Next _ xs) = xs