{-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Core.Utils.JoinList -- Copyright : (c) Stephen Tetley 2010 -- License : BSD3 -- -- A \"join list\" datatype and operations. -- -- A join list is implemented a binary tree, so joining two -- lists (catenation, aka (++)) is a cheap operation. -- -- This constrasts with the regular list datatype which is a -- cons list: while consing on a regular list is by nature cheap, -- joining (++) is expensive. -- -- This version has no Empty constructor, so empty join lists -- cannot be built. -- -------------------------------------------------------------------------------- module Wumpus.Core.Utils.JoinList ( -- * Join list datatype, opaque. JoinList -- * Left view as per Data.Sequence , ViewL(..) -- * Conversion between join lists and regular lists , fromList , toList , toListF -- * Construction , one , cons , snoc , join -- * Basic functions , head , accumMapL , isOne , isMany -- * Left view , viewl ) where import Control.Applicative hiding ( empty ) import Data.Foldable ( Foldable ) import qualified Data.Foldable as F import Data.Monoid ( mappend ) import Data.Traversable ( Traversable(..) ) import Prelude hiding ( head ) data JoinList a = One a | Join (JoinList a) (JoinList a) deriving (Eq) data ViewL a = OneL a | a :< (JoinList a) deriving (Eq,Show) -------------------------------------------------------------------------------- instance Show a => Show (JoinList a) where showsPrec _ xs = showString "fromList " . shows (toList xs) instance Functor JoinList where fmap f (One a) = One (f a) fmap f (Join t u) = Join (fmap f t) (fmap f u) instance Foldable JoinList where foldMap f (One a) = f a foldMap f (Join t u) = F.foldMap f t `mappend` F.foldMap f u foldr = joinfoldr foldl = joinfoldl instance Traversable JoinList where traverse f (One a) = One <$> f a traverse f (Join t u) = Join <$> traverse f t <*> traverse f u -- Views instance Functor ViewL where fmap f (OneL a) = OneL $ f a fmap f (a :< as) = f a :< fmap f as -------------------------------------------------------------------------------- -- Conversion -- | Convert a join list to a regular list. -- toList :: JoinList a -> [a] toList = joinfoldl (flip (:)) [] -- | Build a join list from a regular list. -- -- This builds a tall skinny list. -- -- WARNING - throws an error on empty list. -- fromList :: [a] -> JoinList a fromList [] = error "Wumpus.Core - internal error empty JoinList" fromList [x] = One x fromList (x:xs) = Join (One x) (fromList xs) -- Note -- this works from Right to left... -- toListF :: (a -> b) -> JoinList a -> [b] toListF f = step [] where step acc (One x) = f x : acc step acc (Join t u) = let acc' = step acc u in step acc' t -------------------------------------------------------------------------------- isOne :: JoinList a -> Bool isOne (One _) = True isOne _ = False isMany :: JoinList a -> Bool isMany (Join _ _) = True isMany _ = False -- | Create a singleton join list. -- one :: a -> JoinList a one = One -- | Cons an element to the front of the join list. -- cons :: a -> JoinList a -> JoinList a cons a xs = Join (One a) xs -- | Snoc an element to the tail of the join list. -- snoc :: JoinList a -> a -> JoinList a snoc xs a = Join xs (One a) infixr 5 `join` -- | Because there is no empty join list, join is Join. -- join :: JoinList a -> JoinList a -> JoinList a join = Join -------------------------------------------------------------------------------- -- Basic functions -- | Extract the first element of a join list - i.e. the leftmost -- element of the left spine. An error is thrown if the list is -- empty. -- -- This function performs a traversal down the left spine, so -- unlike @head@ on regular lists this function is not performed -- in constant time. -- head :: JoinList a -> a head (One a) = a head (Join t _) = head t accumMapL :: (x -> st -> (y,st)) -> JoinList x -> st -> (JoinList y,st) accumMapL f xs st0 = go xs st0 where go (One x) st = let (y,st') = f x st in (One y,st') go (Join t u) st = (Join v w, st'') where (v,st') = go t st (w,st'') = go u st' -- | Right-associative fold of a JoinList. -- joinfoldr :: (a -> b -> b) -> b -> JoinList a -> b joinfoldr f = go where go e (One a) = f a e go e (Join t u) = go (go e t) u -- | Left-associative fold of a JoinList. -- joinfoldl :: (b -> a -> b) -> b -> JoinList a -> b joinfoldl f = go where go e (One a) = f e a go e (Join t u) = go (go e u) t -------------------------------------------------------------------------------- -- Views -- | Access the left end of a sequence. -- -- Unlike the corresponing operation on Data.Sequence this is -- not a cheap operation, the joinlist must be traversed down -- the left spine to find the leftmost node. -- -- Also the traversal may involve changing the shape of the -- underlying binary tree. -- viewl :: JoinList a -> ViewL a viewl (One a) = OneL a viewl (Join t u) = step t u where step (One a) r = a :< r step (Join t' u') r = step t' (Join u' r)