content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
  {-# LANGUAGE DefaultSignatures #-}
  {-# LANGUAGE DeriveDataTypeable #-}
  {-# LANGUAGE StandaloneDeriving #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  -- | "Data.NonNull" extends the concepts from
  -- "Data.List.NonEmpty" to any 'MonoFoldable'.
  --
  -- 'NonNull' is a newtype wrapper for a container with 1 or more elements.
  module Data.NonNull (
      NonNull
    , fromNullable
    , impureNonNull
    , nonNull
    , toNullable
    , fromNonEmpty
    , toNonEmpty
    , ncons
    , nuncons
    , splitFirst
    , nfilter
    , nfilterM
    , nReplicate
    , head
    , tail
    , last
    , init
    , ofoldMap1
    , ofold1
    , ofoldr1
    , ofoldl1'
    , maximum
    , maximumBy
    , minimum
    , minimumBy
    , (<|)
    , toMinList
    , mapNonNull
    , GrowingAppend
  ) where
  
  import Prelude hiding (head, tail, init, last, reverse, seq, filter, replicate, maximum, minimum)
  import Control.Arrow (second)
  import Control.Exception.Base (Exception, throw)
  import Data.Data
  import qualified Data.List.NonEmpty as NE
  import Data.Maybe (fromMaybe)
  import Data.MonoTraversable
  import Data.Sequences
  import Control.Monad.Trans.State.Strict (evalState, state)
  
  data NullError = NullError String deriving (Show, Typeable)
  instance Exception NullError
  
  -- | A monomorphic container that is not null.
  newtype NonNull mono = NonNull
      { toNullable :: mono
      -- ^ __Safely__ convert from a non-null monomorphic container to a nullable monomorphic container.
      }
      deriving (Eq, Ord, Read, Show, Data, Typeable)
  type instance Element (NonNull mono) = Element mono
  deriving instance MonoFunctor mono => MonoFunctor (NonNull mono)
  deriving instance MonoFoldable mono => MonoFoldable (NonNull mono)
  instance MonoTraversable mono => MonoTraversable (NonNull mono) where
      otraverse f (NonNull x) = fmap NonNull (otraverse f x)
      {-# INLINE otraverse #-}
  
  instance GrowingAppend mono => GrowingAppend (NonNull mono)
  
  instance (Semigroup mono, GrowingAppend mono) => Semigroup (NonNull mono) where
      NonNull x <> NonNull y = NonNull (x <> y)
  
  instance SemiSequence seq => SemiSequence (NonNull seq) where
      type Index (NonNull seq) = Index seq
  
      intersperse e = unsafeMap $ intersperse e
      reverse       = unsafeMap reverse
      find f        = find f . toNullable
      cons x        = unsafeMap $ cons x
      snoc xs x     = unsafeMap (flip snoc x) xs
      sortBy f      = unsafeMap $ sortBy f
  
  -- | This function is unsafe, and must not be exposed from this module.
  unsafeMap :: (mono -> mono) -> NonNull mono -> NonNull mono
  unsafeMap f (NonNull x) = NonNull (f x)
  
  instance MonoPointed mono => MonoPointed (NonNull mono) where
      opoint = NonNull . opoint
      {-# INLINE opoint #-}
  instance IsSequence mono => MonoComonad (NonNull mono) where
          oextract  = head
          oextend f (NonNull mono) = NonNull
                                   . flip evalState mono
                                   . ofor mono
                                   . const
                                   . state
                                   $ \mono' -> (f (NonNull mono'), tailEx mono')
  
  -- | __Safely__ convert from an __unsafe__ monomorphic container to a __safe__
  -- non-null monomorphic container.
  fromNullable :: MonoFoldable mono => mono -> Maybe (NonNull mono)
  fromNullable mono
      | onull mono = Nothing
      | otherwise = Just (NonNull mono)
  
  -- | __Unsafely__ convert from an __unsafe__ monomorphic container to a __safe__
  -- non-null monomorphic container.
  --
  -- Throws an exception if the monomorphic container is empty.
  --
  -- @since 1.0.0
  impureNonNull :: MonoFoldable mono => mono -> NonNull mono
  impureNonNull nullable =
    fromMaybe (throw $ NullError "Data.NonNull.impureNonNull (NonNull default): expected non-null")
            $ fromNullable nullable
  
  -- | Old synonym for 'impureNonNull'
  nonNull :: MonoFoldable mono => mono -> NonNull mono
  nonNull = impureNonNull
  {-# DEPRECATED nonNull "Please use the more explicit impureNonNull instead" #-}
  
  -- | __Safely__ convert from a 'NonEmpty' list to a non-null monomorphic container.
  fromNonEmpty :: IsSequence seq => NE.NonEmpty (Element seq) -> NonNull seq
  fromNonEmpty = impureNonNull . fromList . NE.toList
  {-# INLINE fromNonEmpty #-}
  
  -- | __Safely__ convert from a 'NonNull' container to a 'NonEmpty' list.
  --
  -- @since 1.0.15.0
  toNonEmpty :: MonoFoldable mono => NonNull mono -> NE.NonEmpty (Element mono)
  toNonEmpty = NE.fromList . otoList
  
  -- | Specializes 'fromNonEmpty' to lists only.
  toMinList :: NE.NonEmpty a -> NonNull [a]
  toMinList = fromNonEmpty
  
  -- | Prepend an element to a 'SemiSequence', creating a non-null 'SemiSequence'.
  --
  -- Generally this uses cons underneath.
  -- cons is not efficient for most data structures.
  --
  -- Alternatives:
  --
  -- * if you don't need to cons, use 'fromNullable' or 'nonNull' if you can create your structure in one go.
  -- * if you need to cons, you might be able to start off with an efficient data structure such as a 'NonEmpty' List.
  --     'fromNonEmpty' will convert that to your data structure using the structure's fromList function.
  ncons :: SemiSequence seq => Element seq -> seq -> NonNull seq
  ncons x xs = nonNull $ cons x xs
  
  -- | Extract the first element of a sequence and the rest of the non-null sequence if it exists.
  nuncons :: IsSequence seq => NonNull seq -> (Element seq, Maybe (NonNull seq))
  nuncons xs =
    second fromNullable
      $ fromMaybe (error "Data.NonNull.nuncons: data structure is null, it should be non-null")
                $ uncons (toNullable xs)
  
  -- | Same as 'nuncons' with no guarantee that the rest of the sequence is non-null.
  splitFirst :: IsSequence seq => NonNull seq -> (Element seq, seq)
  splitFirst xs =
    fromMaybe (error "Data.NonNull.splitFirst: data structure is null, it should be non-null")
            $ uncons (toNullable xs)
  
  -- | Equivalent to @"Data.Sequences".'Data.Sequences.filter'@,
  -- but works on non-nullable sequences.
  nfilter :: IsSequence seq => (Element seq -> Bool) -> NonNull seq -> seq
  nfilter f = filter f . toNullable
  
  -- | Equivalent to @"Data.Sequences".'Data.Sequences.filterM'@,
  -- but works on non-nullable sequences.
  nfilterM :: (Monad m, IsSequence seq) => (Element seq -> m Bool) -> NonNull seq -> m seq
  nfilterM f = filterM f . toNullable
  
  -- | Equivalent to @"Data.Sequences".'Data.Sequences.replicate'@
  --
  -- @i@ must be @> 0@
  --
  -- @i <= 0@ is treated the same as providing @1@
  nReplicate :: IsSequence seq => Index seq -> Element seq -> NonNull seq
  nReplicate i = nonNull . replicate (max 1 i)
  
  -- | __Safe__ version of 'tailEx', only working on non-nullable sequences.
  tail :: IsSequence seq => NonNull seq -> seq
  tail = tailEx . toNullable
  {-# INLINE tail #-}
  
  -- | __Safe__ version of 'initEx', only working on non-nullable sequences.
  init :: IsSequence seq => NonNull seq -> seq
  init = initEx . toNullable
  {-# INLINE init #-}
  
  infixr 5 <|
  
  -- | Prepend an element to a non-null 'SemiSequence'.
  (<|) :: SemiSequence seq => Element seq -> NonNull seq -> NonNull seq
  x <| y = ncons x (toNullable y)
  
  -- | Return the first element of a monomorphic container.
  --
  -- Safe version of 'headEx', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  head :: MonoFoldable mono => NonNull mono -> Element mono
  head = headEx . toNullable
  {-# INLINE head #-}
  
  -- | Return the last element of a monomorphic container.
  --
  -- Safe version of 'lastEx', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  last :: MonoFoldable mono => NonNull mono -> Element mono
  last = lastEx . toNullable
  {-# INLINE last #-}
  
  -- | Map each element of a monomorphic container to a semigroup, and combine the
  -- results.
  --
  -- Safe version of 'ofoldMap1Ex', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  --
  -- ==== __Examples__
  --
  -- @
  -- > let xs = ncons ("hello", 1 :: 'Integer') [(" world", 2)]
  -- > 'ofoldMap1' 'fst' xs
  -- "hello world"
  -- @
  ofoldMap1 :: (MonoFoldable mono, Semigroup m) => (Element mono -> m) -> NonNull mono -> m
  ofoldMap1 f = ofoldMap1Ex f . toNullable
  {-# INLINE ofoldMap1 #-}
  
  -- | Join a monomorphic container, whose elements are 'Semigroup's, together.
  --
  -- Safe, only works on monomorphic containers wrapped in a 'NonNull'.
  --
  -- ==== __Examples__
  --
  -- @
  -- > let xs = ncons "a" ["b", "c"]
  -- > xs
  -- 'NonNull' {toNullable = ["a","b","c"]}
  --
  -- > 'ofold1' xs
  -- "abc"
  -- @
  ofold1 :: (MonoFoldable mono, Semigroup (Element mono)) => NonNull mono -> Element mono
  ofold1 = ofoldMap1 id
  {-# INLINE ofold1 #-}
  
  -- | Right-associative fold of a monomorphic container with no base element.
  --
  -- Safe version of 'ofoldr1Ex', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  --
  -- @'foldr1' f = "Prelude".'Prelude.foldr1' f . 'otoList'@
  --
  -- ==== __Examples__
  --
  -- @
  -- > let xs = ncons "a" ["b", "c"]
  -- > 'ofoldr1' (++) xs
  -- "abc"
  -- @
  ofoldr1 :: MonoFoldable mono
          => (Element mono -> Element mono -> Element mono)
          -> NonNull mono
          -> Element mono
  ofoldr1 f = ofoldr1Ex f . toNullable
  {-# INLINE ofoldr1 #-}
  
  -- | Strict left-associative fold of a monomorphic container with no base
  -- element.
  --
  -- Safe version of 'ofoldl1Ex'', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  --
  -- @'foldl1'' f = "Prelude".'Prelude.foldl1'' f . 'otoList'@
  --
  -- ==== __Examples__
  --
  -- @
  -- > let xs = ncons "a" ["b", "c"]
  -- > 'ofoldl1'' (++) xs
  -- "abc"
  -- @
  ofoldl1' :: MonoFoldable mono
           => (Element mono -> Element mono -> Element mono)
           -> NonNull mono
           -> Element mono
  ofoldl1' f = ofoldl1Ex' f . toNullable
  {-# INLINE ofoldl1' #-}
  
  -- | Get the maximum element of a monomorphic container.
  --
  -- Safe version of 'maximumEx', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  --
  -- ==== __Examples__
  --
  -- @
  -- > let xs = ncons 1 [2, 3 :: Int]
  -- > 'maximum' xs
  -- 3
  -- @
  maximum :: (MonoFoldable mono, Ord (Element mono))
          => NonNull mono
          -> Element mono
  maximum = maximumEx . toNullable
  {-# INLINE maximum #-}
  
  -- | Get the minimum element of a monomorphic container.
  --
  -- Safe version of 'minimumEx', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  --
  -- ==== __Examples__
  --
  -- @
  -- > let xs = ncons 1 [2, 3 :: Int]
  -- > 'minimum' xs
  -- 1
  -- @
  minimum :: (MonoFoldable mono, Ord (Element mono))
          => NonNull mono
          -> Element mono
  minimum = minimumEx . toNullable
  {-# INLINE minimum #-}
  
  -- | Get the maximum element of a monomorphic container,
  -- using a supplied element ordering function.
  --
  -- Safe version of 'maximumByEx', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  maximumBy :: MonoFoldable mono
            => (Element mono -> Element mono -> Ordering)
            -> NonNull mono
            -> Element mono
  maximumBy cmp = maximumByEx cmp . toNullable
  {-# INLINE maximumBy #-}
  
  -- | Get the minimum element of a monomorphic container,
  -- using a supplied element ordering function.
  --
  -- Safe version of 'minimumByEx', only works on monomorphic containers wrapped in a
  -- 'NonNull'.
  minimumBy :: MonoFoldable mono
            => (Element mono -> Element mono -> Ordering)
            -> NonNull mono
            -> Element mono
  minimumBy cmp = minimumByEx cmp . toNullable
  {-# INLINE minimumBy #-}
  
  -- | 'fmap' over the underlying container in a 'NonNull'.
  --
  -- @since 1.0.6.0
  
  -- ==== __Examples__
  --
  -- @
  -- > let xs = 'ncons' 1 [2, 3 :: Int]
  -- > 'mapNonNull' 'show' xs
  -- 'NonNull' {toNullable = [\"1\",\"2\",\"3\"]}
  -- @
  mapNonNull :: (Functor f, MonoFoldable (f b))
             => (a -> b)
             -> NonNull (f a)
             -> NonNull (f b)
  mapNonNull f = impureNonNull . fmap f . toNullable
  