ral-0.1: src/Data/RAList/Internal.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.RAList.Internal (
RAList (..),
-- * Showing
explicitShow,
explicitShowsPrec,
-- * Construction
empty,
singleton,
cons,
-- * Indexing
(!),
(!?),
length,
null,
-- * Conversions
toList,
fromList,
-- * Folding
ifoldMap,
-- * Mapping
adjust,
map,
imap,
itraverse,
) where
import Prelude
(Bool (..), Eq, Functor (..), Int, Maybe (..), Ord (..), Show (..),
ShowS, String, showParen, showString, ($), (.))
import Control.Applicative (Applicative (..), (<$>))
import Control.DeepSeq (NFData (..))
import Control.Exception (ArrayException (IndexOutOfBounds), throw)
import Data.Hashable (Hashable (..))
import Data.List.NonEmpty (NonEmpty (..))
import Data.Monoid (Monoid (..))
import Data.Semigroup (Semigroup (..))
import qualified Data.Foldable as I (Foldable (..))
import qualified Data.Traversable as I (Traversable (..))
import qualified Test.QuickCheck as QC
import qualified Data.RAList.NonEmpty.Internal as NE
-- $setup
-- >>> import Data.Char (toUpper)
-------------------------------------------------------------------------------
-- Type
-------------------------------------------------------------------------------
-- | Random access list.
data RAList a
= Empty
| NonEmpty (NE.NERAList a)
deriving (Eq, Ord, Functor, I.Traversable)
-------------------------------------------------------------------------------
-- Instances
-------------------------------------------------------------------------------
-- |
--
-- >>> I.length $ fromList $ ['a' .. 'z']
-- 26
--
instance I.Foldable RAList where
foldMap _ Empty = mempty
foldMap f (NonEmpty xs) = I.foldMap f xs
#if MIN_VERSION_base(4,8,0)
length = length
null = null
#endif
instance NFData a => NFData (RAList a) where
rnf Empty = ()
rnf (NonEmpty xs) = rnf xs
instance Hashable a => Hashable (RAList a) where
hashWithSalt salt Empty = hashWithSalt salt (0 :: Int)
hashWithSalt salt (NonEmpty r) = hashWithSalt salt r
-- |
--
-- >>> fromList "abc" <> fromList "xyz"
-- fromList "abcxyz"
--
instance Semigroup (RAList a) where
Empty <> ys = ys
xs <> Empty = xs
NonEmpty xs <> NonEmpty ys = NonEmpty (xs <> ys)
instance Monoid (RAList a) where
mempty = Empty
mappend = (<>)
-- TODO: Applicative, Monad
#ifdef MIN_VERSION_semigroupoids
-- Apply, Bind
#endif
-------------------------------------------------------------------------------
-- Showing
-------------------------------------------------------------------------------
instance Show a => Show (RAList a) where
showsPrec d xs = showParen (d > 10) $ showString "fromList " . showsPrec 11 (toList xs)
explicitShow :: Show a => RAList a -> String
explicitShow xs = explicitShowsPrec 0 xs ""
explicitShowsPrec :: Show a => Int -> RAList a -> ShowS
explicitShowsPrec _ Empty = showString "Empty"
explicitShowsPrec d (NonEmpty xs) = showParen (d > 10) $ showString "NonEmpty " . NE.explicitShowsPrec 11 xs
-------------------------------------------------------------------------------
-- Construction
-------------------------------------------------------------------------------
-- | Empty 'RAList'.
--
-- >>> empty :: RAList Int
-- fromList []
--
empty :: RAList a
empty = Empty
-- | Single element 'RAList'.
singleton :: a -> RAList a
singleton = NonEmpty . NE.singleton
-- | 'cons' for non-empty rals.
cons :: a -> RAList a -> RAList a
cons x Empty = singleton x
cons x (NonEmpty xs) = NonEmpty (NE.cons x xs)
toList :: RAList a -> [a]
toList Empty = []
toList (NonEmpty xs) = I.foldr (:) [] xs
-- |
--
-- >>> fromList ['a' .. 'f']
-- fromList "abcdef"
--
-- >>> explicitShow $ fromList ['a' .. 'f']
-- "NonEmpty (NE (Cons0 (Cons1 (Nd (Lf 'a') (Lf 'b')) (Last (Nd (Nd (Lf 'c') (Lf 'd')) (Nd (Lf 'e') (Lf 'f')))))))"
--
fromList :: [a] -> RAList a
fromList [] = Empty
fromList (x:xs) = NonEmpty (NE.fromNonEmpty (x :| xs))
-------------------------------------------------------------------------------
-- Indexing
-------------------------------------------------------------------------------
-- | List index.
--
--- >>> fromList ['a'..'f'] ! 0
-- 'a'
--
-- >>> fromList ['a'..'f'] ! 5
-- 'f'
--
-- >>> fromList ['a'..'f'] ! 6
-- *** Exception: array index out of range: RAList
-- ...
--
(!) :: RAList a -> Int -> a
(!) Empty _ = throw $ IndexOutOfBounds "RAList"
(!) (NonEmpty xs) i = xs NE.! i
-- | safe list index.
--
-- >>> fromList ['a'..'f'] !? 0
-- Just 'a'
--
-- >>> fromList ['a'..'f'] !? 5
-- Just 'f'
--
-- >>> fromList ['a'..'f'] !? 6
-- Nothing
--
(!?) :: RAList a -> Int -> Maybe a
Empty !? _ = Nothing
NonEmpty xs !? i = xs NE.!? i
length :: RAList a -> Int
length Empty = 0
length (NonEmpty xs) = NE.length xs
null :: RAList a -> Bool
null Empty = True
null (NonEmpty _) = False
-------------------------------------------------------------------------------
-- Folds
-------------------------------------------------------------------------------
ifoldMap :: Monoid m => (Int -> a -> m) -> RAList a -> m
ifoldMap _ Empty = mempty
ifoldMap f (NonEmpty r) = NE.ifoldMap f r
-------------------------------------------------------------------------------
-- Mapping
-------------------------------------------------------------------------------
-- |
-- >>> map toUpper (fromList ['a'..'f'])
-- fromList "ABCDEF"
--
map :: (a -> b) -> RAList a -> RAList b
map = fmap
-- |
--
-- >>> imap (,) $ fromList ['a' .. 'f']
-- fromList [(0,'a'),(1,'b'),(2,'c'),(3,'d'),(4,'e'),(5,'f')]
imap :: (Int -> a -> b) -> RAList a -> RAList b
imap f xs = unI (itraverse (\i x -> I (f i x)) xs)
itraverse :: forall f a b. Applicative f => (Int -> a -> f b) -> RAList a -> f (RAList b)
itraverse _ Empty = pure Empty
itraverse f (NonEmpty xs) = NonEmpty <$> NE.itraverse f xs
-- | Adjust a value in the list.
--
-- >>> adjust 3 toUpper $ fromList "bcdef"
-- fromList "bcdEf"
--
-- If index is out of bounds, the list is returned unmodified.
--
-- >>> adjust 10 toUpper $ fromList "bcdef"
-- fromList "bcdef"
--
-- >>> adjust (-1) toUpper $ fromList "bcdef"
-- fromList "bcdef"
--
adjust :: forall a. Int -> (a -> a) -> RAList a -> RAList a
adjust _ _ Empty = Empty
adjust i f (NonEmpty xs) = NonEmpty (NE.adjust i f xs)
-------------------------------------------------------------------------------
-- QuickCheck
-------------------------------------------------------------------------------
instance QC.Arbitrary1 RAList where
liftArbitrary = fmap fromList . QC.liftArbitrary
liftShrink shr = fmap fromList . QC.liftShrink shr . toList
instance QC.Arbitrary a => QC.Arbitrary (RAList a) where
arbitrary = QC.arbitrary1
shrink = QC.shrink1
instance QC.CoArbitrary a => QC.CoArbitrary (RAList a) where
coarbitrary = QC.coarbitrary . toList
instance QC.Function a => QC.Function (RAList a) where
function = QC.functionMap toList fromList
-------------------------------------------------------------------------------
-- Utilities
-------------------------------------------------------------------------------
newtype I a = I a
unI :: I a -> a
unI (I a) = a
instance Functor I where
fmap f (I x) = I (f x)
instance Applicative I where
pure = I
I f <*> I x = I (f x)
_ *> x = x
x <* _ = x
#if MIN_VERSION_base(4,10,0)
liftA2 f (I x) (I y) = I (f x y)
#endif