{-# LANGUAGE CPP #-}
-- | Extra list functions and list-like types.
module Data.List.Extra where

import Control.DeepSeq (NFData(..))
import Data.Traversable (fmapDefault, foldMapDefault)

#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>), (<*>))
import Data.Foldable (Foldable(..))
import Data.Traversable (Traversable(..))
#else
-- Why does this give a redundancy warning? It's necessary in order to
-- define the toList function in the Foldable instance for NonEmpty!
import Data.Foldable (toList)
#endif

-- * Regular lists

-- | Check if a list has more than some number of elements.
moreThan :: [a] -> Int -> Bool
moreThan [] n = n < 0
moreThan _ 0  = True
moreThan (_:xs) n = moreThan xs (n-1)

-- * Non-empty lists

-- This gets exposed to users of the library, so it has a bunch of
-- classes which aren't actually used in the rest of the code to make
-- it more friendly to further use.

-- | The type of non-empty lists.
data NonEmpty a = a :| [a] deriving (Eq, Ord, Read, Show)

instance Functor NonEmpty where
  fmap = fmapDefault

instance Foldable NonEmpty where
  foldMap = foldMapDefault

#if __GLASGOW_HASKELL__ >= 710
  -- toList isn't in Foldable until GHC 7.10
  toList (a :| as) = a : as
#endif

instance Traversable NonEmpty where
  traverse f (a:|as) = (:|) <$> f a <*> traverse f as

instance NFData a => NFData (NonEmpty a) where
  rnf (x:|xs) = rnf (x, xs)

-- | Convert a 'NonEmpty' to a regular non-empty list.
toList :: NonEmpty a -> [a]
toList (a :| as) = a : as

-- | Convert a regular non-empty list to a 'NonEmpty'. This is
-- necessarily partial.
unsafeToNonEmpty :: [a] -> NonEmpty a
unsafeToNonEmpty (a:as) = a :| as
unsafeToNonEmpty [] = error "Cannot convert [] to NonEmpty!"