module Data.Semilattice where

import           Data.Functor.Compose
import           Data.Functor.Const
import           Data.Functor.Product
import qualified Data.HashMap.Monoidal as HashMap
import           Data.Hashable (Hashable)
import qualified Data.IntMap.Monoidal as LazyIntMap
import qualified Data.IntMap.Monoidal.Strict as StrictIntMap
import qualified Data.Map.Monoidal as LazyMap
import qualified Data.Map.Monoidal.Strict as StrictMap
import           Data.Monoid hiding (Product)
import           Data.Semigroup hiding (Product)
import           Data.Set (Set)
import           GHC.Generics

------------------------------------------------------------------------------
-- | A 'Semilattice' is a 'Monoid' with the additional property that its
-- @'(/\)' = '(<>)'@ operation is commutative and idempotent.  That is:
--
-- @
-- a '/\' b = b '/\' a
-- @
--
-- and
--
-- @
-- a '/\' a = a
-- @
--
-- These two properties ensure the internal representations of
-- 'Data.QuadTree.QuadTree' and 'Data.OctTree.OctTree' can't leak out when
-- performing spatial queries.
class Monoid a => Semilattice a where
  (/\) :: a -> a -> a
  (/\) = forall a. Semigroup a => a -> a -> a
(<>)

instance Semilattice ()
instance Semilattice Any
instance Semilattice All
instance (Ord a, Bounded a) => Semilattice (Max a)
instance (Ord a, Bounded a) => Semilattice (Min a)
instance Ord a => Semilattice (Set a)
instance (Ord k, Semilattice a) => Semilattice (LazyMap.MonoidalMap k a)
instance (Ord k, Semilattice a) => Semilattice (StrictMap.MonoidalMap k a)
instance Semilattice a => Semilattice (LazyIntMap.MonoidalIntMap a)
instance Semilattice a => Semilattice (StrictIntMap.MonoidalIntMap a)
instance (Hashable k, Eq k, Semilattice a) => Semilattice (HashMap.MonoidalHashMap k a)
instance Semilattice b => Semilattice (a -> b)
instance Semilattice a => Semilattice (Maybe a)
instance Semilattice a => Semilattice (Const a b)
instance Semilattice a => Semilattice (K1 i a b)
instance Semilattice (f a) => Semilattice (M1 i c f a)
instance (Semilattice (f a), Semilattice (g a)) => Semilattice (Product f g a)
instance (Semilattice (f (g a))) => Semilattice (Compose f g a)
instance (Semilattice (f (g a))) => Semilattice ((:.:) f g a)
instance (Semilattice (f a), Semilattice (g a)) => Semilattice ((:*:) f g a)
instance (Semilattice a, Semilattice b) => Semilattice (a, b)
instance (Semilattice a, Semilattice b, Semilattice c) => Semilattice (a, b, c)
instance (Semilattice a, Semilattice b, Semilattice c, Semilattice d) => Semilattice (a, b, c, d)
instance (Semilattice a, Semilattice b, Semilattice c, Semilattice d, Semilattice e) => Semilattice (a, b, c, d, e)