packages feed

semilattices-0.0.0.7: src/Data/Semilattice/Meet.hs

{-# LANGUAGE DeriveTraversable, GeneralizedNewtypeDeriving #-}
-- | Join semilattices, related to 'Data.Semilattice.Lower.Lower' and 'Upper'.
module Data.Semilattice.Meet
( Meet(..)
, Meeting(..)
, GreaterThan(..)
) where

import Data.HashMap.Lazy as HashMap
import Data.HashSet as HashSet
import Data.IntMap as IntMap
import Data.IntSet as IntSet
import Data.Map as Map
import Data.Semigroup
import Data.Semilattice.Upper
import Data.Set as Set

-- | A meet semilattice is an idempotent commutative semigroup.
class Meet s where
  -- | The meet operation.
  --
  --   Laws:
  --
  --   Idempotence:
  --
  -- @
  -- x '/\' x = x
  -- @
  --
  --   Associativity:
  --
  -- @
  -- a '/\' (b '/\' c) = (a '/\' b) '/\' c
  -- @
  --
  --   Commutativity:
  --
  -- @
  -- a '/\' b = b '/\' a
  -- @
  --
  --   Additionally, if @s@ has an 'Upper' bound, then 'upperBound' must be its identity:
  --
  -- @
  -- 'upperBound' '/\' a = a
  -- a '/\' 'upperBound' = a
  -- @
  --
  --   If @s@ has a 'Data.Semilattice.Lower.Lower' bound, then 'Data.Semilattice.Lower.lowerBound' must be its absorbing element:
  --
  -- @
  -- 'Data.Semilattice.Lower.lowerBound' '/\' a = 'Data.Semilattice.Lower.lowerBound'
  -- a '/\' 'Data.Semilattice.Lower.lowerBound' = 'Data.Semilattice.Lower.lowerBound'
  -- @
  (/\) :: s -> s -> s

  infixr 7 /\


-- Prelude

instance Meet () where
  _ /\ _ = ()

-- | Boolean conjunction forms a semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: Bool)
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: Bool)
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: Bool)
--
--   Identity:
--
--   prop> upperBound /\ a == (a :: Bool)
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: Bool)
instance Meet Bool where
  (/\) = (&&)

-- | Orderings form a semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: Ordering)
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: Ordering)
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: Ordering)
--
--   Identity:
--
--   prop> upperBound /\ a == (a :: Ordering)
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: Ordering)
instance Meet Ordering where
  LT /\ _ = LT
  _ /\ LT = LT
  GT /\ b = b
  a /\ GT = a
  _ /\ _ = EQ

-- | Functions with semilattice codomains form a semilattice.
--
--   Idempotence:
--
--   prop> \ (Fn x) -> x /\ x ~= (x :: Int -> Bool)
--
--   Associativity:
--
--   prop> \ (Fn a) (Fn b) (Fn c) -> a /\ (b /\ c) ~= (a /\ b) /\ (c :: Int -> Bool)
--
--   Commutativity:
--
--   prop> \ (Fn a) (Fn b) -> a /\ b ~= b /\ (a :: Int -> Bool)
--
--   Identity:
--
--   prop> \ (Fn a) -> upperBound /\ a ~= (a :: Int -> Bool)
--
--   Absorption:
--
--   prop> \ (Fn a) -> lowerBound /\ a ~= (lowerBound :: Int -> Bool)
instance Meet b => Meet (a -> b) where
  f /\ g = (/\) <$> f <*> g


-- Data.Semigroup

-- | The greatest lowerBound bound gives rise to a meet semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: Min Int)
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: Min Int)
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: Min Int)
--
--   Identity:
--
--   prop> upperBound /\ a == (a :: Min Int)
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: Min Int)
instance Ord a => Meet (Min a) where
  (/\) = (<>)


-- containers

-- | IntMap union with 'Meet'able values forms a semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: IntMap (Set Char))
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: IntMap (Set Char))
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: IntMap (Set Char))
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: IntMap (Set Char))
instance Meet a => Meet (IntMap a) where
  (/\) = IntMap.intersectionWith (/\)

-- | IntSet intersection forms a semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: IntSet)
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: IntSet)
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: IntSet)
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: IntSet)
instance Meet IntSet where
  (/\) = IntSet.intersection

-- | Map union with 'Meet'able values forms a semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: Map Char (Set Char))
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: Map Char (Set Char))
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: Map Char (Set Char))
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: Map Char (Set Char))
instance (Ord k, Meet a) => Meet (Map k a) where
  (/\) = Map.intersectionWith (/\)

-- | Set intersection forms a semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: Set Char)
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: Set Char)
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: Set Char)
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: Set Char)
instance Ord a => Meet (Set a) where
  (/\) = Set.intersection


-- unordered-containers

-- | HashMap union with 'Meet'able values forms a semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: HashMap Char (Set Char))
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: HashMap Char (Set Char))
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: HashMap Char (Set Char))
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: HashMap Char (Set Char))
instance (Eq k, Meet a) => Meet (HashMap k a) where
  (/\) = HashMap.intersectionWith (/\)

-- | HashSet intersection forms a semilattice.
--
--   Idempotence:
--
--   prop> x /\ x == (x :: HashSet Char)
--
--   Associativity:
--
--   prop> a /\ (b /\ c) == (a /\ b) /\ (c :: HashSet Char)
--
--   Commutativity:
--
--   prop> a /\ b == b /\ (a :: HashSet Char)
--
--   Absorption:
--
--   prop> lowerBound /\ a == (lowerBound :: HashSet Char)
instance Eq a => Meet (HashSet a) where
  (/\) = HashSet.intersection


-- | A 'Semigroup' for any 'Meet' semilattice.
--
--   If the semilattice has an 'Upper' bound, there is additionally a 'Monoid' instance.
newtype Meeting a = Meeting { getMeeting :: a }
  deriving (Bounded, Enum, Eq, Foldable, Functor, Meet, Num, Ord, Read, Show, Traversable, Upper)

-- | 'Meeting' '<>' is associative.
--
--   prop> Meeting a <> (Meeting b <> Meeting c) == (Meeting a <> Meeting b) <> Meeting (c :: IntSet)
instance Meet a => Semigroup (Meeting a) where
  (<>) = (/\)

-- | 'Meeting' 'mempty' is the left- and right-identity.
--
--   prop> let (l, r) = (mappend mempty (Meeting x), mappend (Meeting x) mempty) in l == Meeting x && r == Meeting (x :: Bool)
instance (Upper a, Meet a) => Monoid (Meeting a) where
  mappend = (<>)
  mempty = upperBound


-- | 'Meet' semilattices give rise to a partial 'Ord'ering.
newtype GreaterThan a = GreaterThan { getGreaterThan :: a }
  deriving (Enum, Eq, Foldable, Functor, Meet, Num, Read, Show, Traversable)

-- | NB: This is not in general a total ordering.
instance (Eq a, Meet a) => Ord (GreaterThan a) where
  compare a b
    | a == b      = EQ
    | a /\ b == a = LT
    | otherwise   = GT

  a <= b = a /\ b == a


-- $setup
-- >>> import Data.Semilattice.Lower
-- >>> import Test.QuickCheck
-- >>> import Test.QuickCheck.Function
-- >>> import Test.QuickCheck.Instances.UnorderedContainers ()
-- >>> instance Arbitrary a => Arbitrary (Min a) where arbitrary = Min <$> arbitrary
-- >>> :{
-- infix 4 ~=
-- f ~= g = (==) <$> f <*> g
-- :}