packages feed

Agda-2.9.0: src/full/Agda/Utils/Null.hs

{-# OPTIONS_GHC -Wunused-imports #-}

{-# OPTIONS_GHC -fno-warn-orphans #-}

-- | Overloaded @null@ and @empty@ for collections and sequences.

module Agda.Utils.Null where

import Prelude hiding (null)

import Control.Monad          ( when, unless )
import Control.Monad.Except   ( ExceptT )
import Control.Monad.Identity ( Identity(..) )
import Control.Monad.Reader   ( ReaderT )
import Control.Monad.State    ( StateT  )
import Control.Monad.Writer   ( WriterT )
import Control.Monad.Trans    ( lift    )
import Control.Monad.Trans.Maybe

import Data.Maybe             ( isNothing )

import Data.ByteString.Char8 qualified as ByteStringChar8
import Data.ByteString.Lazy qualified as ByteStringLazy

import Data.EnumMap (EnumMap)
import Data.EnumMap qualified as EnumMap
import Data.EnumSet (EnumSet)
import Data.EnumSet qualified as EnumSet
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HashMap
import Data.HashSet (HashSet)
import Data.HashSet qualified as HashSet
import Data.IntMap (IntMap)
import Data.IntMap qualified as IntMap
import Data.IntSet (IntSet)
import Data.IntSet qualified as IntSet
import Data.List qualified as List
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Sequence (Seq)
import Data.Sequence qualified as Seq
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Word (Word32, Word64)

import Data.Text (Text)
import Data.Text qualified as Text

import Text.PrettyPrint.Annotated (Doc, isEmpty)

import Agda.Utils.Bag (Bag)
import Agda.Utils.Bag qualified as Bag
import Agda.Utils.VarSet (VarSet)
import Agda.Utils.VarSet qualified as VarSet

import Agda.Utils.Unsafe (unsafeComparePointers)
import Agda.Utils.Impossible

class Null a where
  empty :: a
  null  :: a -> Bool
  -- ^ Satisfying @null empty == True@.

  -- | The default implementation of 'null' compares with 'empty',
  --   first trying pointer equality, then falling back to 'Eq' equality.
  default null :: Eq a => a -> Bool
  null a = unsafeComparePointers a empty || a == empty

instance Null () where
  empty  = ()
  {-# INLINE empty #-}
  null _ = True
  {-# INLINE null #-}

instance (Null a, Null b) => Null (a,b) where
  empty      = (empty, empty)
  null (a,b) = null a && null b

instance (Null a, Null b, Null c) => Null (a,b,c) where
  empty        = (empty, empty, empty)
  null (a,b,c) = null a && null b && null c

instance (Null a, Null b, Null c, Null d) => Null (a,b,c,d) where
  empty          = (empty, empty, empty, empty)
  null (a,b,c,d) = null a && null b && null c && null d

instance Null ByteStringChar8.ByteString where
  empty = ByteStringChar8.empty
  {-# INLINE empty #-}
  null  = ByteStringChar8.null
  {-# INLINE null #-}

instance Null ByteStringLazy.ByteString where
  empty = ByteStringLazy.empty
  {-# INLINE empty #-}
  null  = ByteStringLazy.null
  {-# INLINE null #-}

instance Null Text where
  empty = Text.empty
  {-# INLINE empty #-}
  null  = Text.null
  {-# INLINE null #-}

instance Null [a] where
  empty = []
  {-# INLINE empty #-}
  null  = List.null
  {-# INLINE null #-}

instance Null (Bag a) where
  empty = Bag.empty
  {-# INLINE empty #-}
  null  = Bag.null
  {-# INLINE null #-}

instance Null (EnumMap k a) where
  empty = EnumMap.empty
  {-# INLINE empty #-}
  null  = EnumMap.null
  {-# INLINE null #-}

instance Null (EnumSet a) where
  empty = EnumSet.empty
  {-# INLINE empty #-}
  null  = EnumSet.null
  {-# INLINE null #-}

instance Null (IntMap a) where
  empty = IntMap.empty
  {-# INLINE empty #-}
  null  = IntMap.null
  {-# INLINE null #-}

instance Null IntSet where
  empty = IntSet.empty
  {-# INLINE empty #-}
  null  = IntSet.null
  {-# INLINE null #-}

instance Null (Map k a) where
  empty = Map.empty
  {-# INLINE empty #-}
  null  = Map.null
  {-# INLINE null #-}

instance Null (HashMap k a) where
  empty = HashMap.empty
  {-# INLINE empty #-}
  null  = HashMap.null
  {-# INLINE null #-}

instance Null (HashSet a) where
  empty = HashSet.empty
  {-# INLINE empty #-}
  null  = HashSet.null
  {-# INLINE null #-}

instance Null (Seq a) where
  empty = Seq.empty
  {-# INLINE empty #-}
  null  = Seq.null
  {-# INLINE null #-}

instance Null (Set a) where
  empty = Set.empty
  {-# INLINE empty #-}
  null  = Set.null
  {-# INLINE null #-}

instance Null VarSet where
  empty = VarSet.empty
  {-# INLINE empty #-}
  null = VarSet.null
  {-# INLINE null #-}

-- | A 'Maybe' is 'null' when it corresponds to the empty list.
instance Null (Maybe a) where
  empty = Nothing
  {-# INLINE empty #-}
  null  = isNothing
  {-# INLINE null #-}

instance Null Word32 where
  empty = 0
  {-# INLINE empty #-}

instance Null Word64 where
  empty = 0
  {-# INLINE empty #-}

-- | Viewing 'Bool' as @'Maybe' ()@, a boolean is 'null' when it is false.
instance Null Bool where
  empty = False
  {-# INLINE empty #-}
  null  = not
  {-# INLINE null #-}

instance Null (Doc a) where
  empty = mempty
  {-# INLINE empty #-}
  null  = isEmpty
  {-# NOINLINE null #-}

-- | 'Either' is seen as error monad.
instance Null a => Null (Either e a) where
  empty = Right empty
  {-# INLINE empty #-}
  null  = either (const False) null
  {-# NOINLINE null #-}

instance Null a => Null (Identity a) where
  empty = return empty
  {-# INLINE empty #-}
  null  = null . runIdentity
  {-# INLINE null #-}

instance Null a => Null (IO a) where
  empty = return empty
  {-# INLINE empty #-}
  null  = __IMPOSSIBLE__
  {-# NOINLINE null #-}

instance (Null (m a), Monad m) => Null (ExceptT e m a) where
  empty = lift empty
  {-# INLINE empty #-}
  null  = __IMPOSSIBLE__
  {-# NOINLINE null #-}

instance (Null (m a), Monad m) => Null (MaybeT m a) where
  empty = lift empty
  {-# INLINE empty #-}
  null  = __IMPOSSIBLE__
  {-# NOINLINE null #-}

instance (Null (m a), Monad m) => Null (ReaderT r m a) where
  empty = lift empty
  {-# INLINE empty #-}
  null  = __IMPOSSIBLE__
  {-# NOINLINE null #-}

instance (Null (m a), Monad m) => Null (StateT s m a) where
  empty = lift empty
  {-# INLINE empty #-}
  null  = __IMPOSSIBLE__
  {-# NOINLINE null #-}

instance (Null (m a), Monad m, Monoid w) => Null (WriterT w m a) where
  empty = lift empty
  {-# INLINE empty #-}
  null  = __IMPOSSIBLE__
  {-# NOINLINE null #-}

-- * Testing for null.

ifNull :: (Null a) => a -> b -> (a -> b) -> b
ifNull a b k = if null a then b else k a

ifNotNull :: (Null a) => a -> (a -> b) -> b -> b
ifNotNull a k b = ifNull a b k

ifNullM :: (Monad m, Null a) => m a -> m b -> (a -> m b) -> m b
ifNullM ma mb k = ma >>= \ a -> ifNull a mb k

ifNotNullM :: (Monad m, Null a) => m a -> (a -> m b) -> m b -> m b
ifNotNullM ma k mb = ifNullM ma mb k

whenNull :: (Monad m, Null a) => a -> m () -> m ()
whenNull = when . null

unlessNull :: (Monad m, Null a) => a -> (a -> m ()) -> m ()
unlessNull a k = unless (null a) $ k a

whenNullM :: (Monad m, Null a) => m a -> m () -> m ()
whenNullM ma k = ma >>= (`whenNull` k)

unlessNullM :: (Monad m, Null a) => m a -> (a -> m ()) -> m ()
unlessNullM ma k = ma >>= (`unlessNull` k)

applyUnlessNull :: (Null a) => a -> (a -> b -> b) -> (b -> b)
applyUnlessNull a f = if null a then id else f a

-- | Disjunction (interpreting @null _@ as @False@).
catchNull :: Null a => a -> a -> a
catchNull a b = if null a then b else a