moonlight-algebra-0.1.0.0: src-abstract/Moonlight/Algebra/Pure/Lattice.hs
-- | The lattice class tower: join/meet semilattices and their bounded forms,
-- lattices, distributive, Heyting and Boolean algebras, with fixpoint helpers.
--
-- Laws: join and meet are idempotent, commutative and associative; bounded
-- variants add an identity; lattices satisfy absorption; distributive adds
-- distributivity; Heyting adds residuation; Boolean adds complementation.
module Moonlight.Algebra.Pure.Lattice
( JoinSemilattice (..),
BoundedJoinSemilattice (..),
MeetSemilattice (..),
BoundedMeetSemilattice (..),
Lattice,
OrderedLattice,
BoundedLattice,
DistributiveLattice,
HeytingAlgebra (..),
BooleanAlgebra (..),
FixpointDivergence (..),
Join (..),
Meet (..),
iterateFixpointFrom,
leastFixpoint,
greatestFixpoint,
leastPreFixpoint,
leastPreFixpointFrom,
greatestPostFixpoint,
greatestPostFixpointFrom,
joinLeq,
meetLeq,
joins,
joins1,
meets,
meets1,
fromBool,
)
where
import Data.IntMap.Strict qualified as IntMap
import Data.IntSet qualified as IntSet
import Data.Kind (Constraint, Type)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Moonlight.Core
( FixpointDivergence (..),
PartialOrder,
fixpointBounded,
)
import Numeric.Natural
( Natural,
)
type Join :: Type -> Type
newtype Join a = Join {getJoin :: a}
deriving stock (Eq, Ord, Show)
type Meet :: Type -> Type
newtype Meet a = Meet {getMeet :: a}
deriving stock (Eq, Ord, Show)
type JoinSemilattice :: Type -> Constraint
class JoinSemilattice a where
join :: a -> a -> a
instance JoinSemilattice () where
join _ _ = ()
instance JoinSemilattice Bool where
join = (||)
instance Ord a => JoinSemilattice (Set.Set a) where
join = Set.union
instance JoinSemilattice IntSet.IntSet where
join = IntSet.union
instance (Ord key, JoinSemilattice value) => JoinSemilattice (Map.Map key value) where
join = Map.unionWith join
instance JoinSemilattice value => JoinSemilattice (IntMap.IntMap value) where
join = IntMap.unionWith join
instance (JoinSemilattice left, JoinSemilattice right) => JoinSemilattice (left, right) where
join (leftA, rightA) (leftB, rightB) =
(join leftA leftB, join rightA rightB)
instance JoinSemilattice value => JoinSemilattice (key -> value) where
join left right key =
join (left key) (right key)
instance JoinSemilattice a => Semigroup (Join a) where
Join left <> Join right =
Join (join left right)
type BoundedJoinSemilattice :: Type -> Constraint
class JoinSemilattice a => BoundedJoinSemilattice a where
bottom :: a
instance BoundedJoinSemilattice () where
bottom = ()
instance BoundedJoinSemilattice Bool where
bottom = False
instance Ord a => BoundedJoinSemilattice (Set.Set a) where
bottom = Set.empty
instance BoundedJoinSemilattice IntSet.IntSet where
bottom = IntSet.empty
instance (Ord key, JoinSemilattice value) => BoundedJoinSemilattice (Map.Map key value) where
bottom = Map.empty
instance JoinSemilattice value => BoundedJoinSemilattice (IntMap.IntMap value) where
bottom = IntMap.empty
instance
(BoundedJoinSemilattice left, BoundedJoinSemilattice right) =>
BoundedJoinSemilattice (left, right)
where
bottom =
(bottom, bottom)
instance BoundedJoinSemilattice value => BoundedJoinSemilattice (key -> value) where
bottom =
const bottom
instance BoundedJoinSemilattice a => Monoid (Join a) where
mempty =
Join bottom
type MeetSemilattice :: Type -> Constraint
class MeetSemilattice a where
meet :: a -> a -> a
instance MeetSemilattice () where
meet _ _ = ()
instance MeetSemilattice Bool where
meet = (&&)
instance Ord a => MeetSemilattice (Set.Set a) where
meet = Set.intersection
instance MeetSemilattice IntSet.IntSet where
meet = IntSet.intersection
instance (Ord key, MeetSemilattice value) => MeetSemilattice (Map.Map key value) where
meet = Map.intersectionWith meet
instance MeetSemilattice value => MeetSemilattice (IntMap.IntMap value) where
meet = IntMap.intersectionWith meet
instance (MeetSemilattice left, MeetSemilattice right) => MeetSemilattice (left, right) where
meet (leftA, rightA) (leftB, rightB) =
(meet leftA leftB, meet rightA rightB)
instance MeetSemilattice value => MeetSemilattice (key -> value) where
meet left right key =
meet (left key) (right key)
instance MeetSemilattice a => Semigroup (Meet a) where
Meet left <> Meet right =
Meet (meet left right)
type BoundedMeetSemilattice :: Type -> Constraint
class MeetSemilattice a => BoundedMeetSemilattice a where
top :: a
instance BoundedMeetSemilattice () where
top = ()
instance BoundedMeetSemilattice Bool where
top = True
instance
(BoundedMeetSemilattice left, BoundedMeetSemilattice right) =>
BoundedMeetSemilattice (left, right)
where
top =
(top, top)
instance BoundedMeetSemilattice value => BoundedMeetSemilattice (key -> value) where
top =
const top
instance BoundedMeetSemilattice a => Monoid (Meet a) where
mempty =
Meet top
type Lattice :: Type -> Constraint
class (JoinSemilattice a, MeetSemilattice a) => Lattice a
type OrderedLattice :: Type -> Constraint
class (PartialOrder a, Lattice a) => OrderedLattice a
type BoundedLattice :: Type -> Constraint
type BoundedLattice a = (Lattice a, BoundedJoinSemilattice a, BoundedMeetSemilattice a)
instance Lattice ()
instance OrderedLattice ()
instance Lattice Bool
instance OrderedLattice Bool
instance Ord a => Lattice (Set.Set a)
instance Ord a => OrderedLattice (Set.Set a)
instance Lattice IntSet.IntSet
instance OrderedLattice IntSet.IntSet
instance (Ord key, Lattice value) => Lattice (Map.Map key value)
instance (Ord key, OrderedLattice value) => OrderedLattice (Map.Map key value)
instance Lattice value => Lattice (IntMap.IntMap value)
instance OrderedLattice value => OrderedLattice (IntMap.IntMap value)
instance (Lattice left, Lattice right) => Lattice (left, right)
instance (OrderedLattice left, OrderedLattice right) => OrderedLattice (left, right)
instance Lattice value => Lattice (key -> value)
type DistributiveLattice :: Type -> Constraint
class Lattice a => DistributiveLattice a
instance DistributiveLattice ()
instance DistributiveLattice Bool
instance Ord a => DistributiveLattice (Set.Set a)
instance DistributiveLattice IntSet.IntSet
instance
(Ord key, DistributiveLattice value) =>
DistributiveLattice (Map.Map key value)
instance DistributiveLattice value => DistributiveLattice (IntMap.IntMap value)
instance
(DistributiveLattice left, DistributiveLattice right) =>
DistributiveLattice (left, right)
instance DistributiveLattice value => DistributiveLattice (key -> value)
infixr 5 <=>
type HeytingAlgebra :: Type -> Constraint
class (BoundedLattice a, DistributiveLattice a) => HeytingAlgebra a where
implies :: a -> a -> a
neg :: a -> a
neg value =
implies value bottom
(<=>) :: a -> a -> a
left <=> right =
meet (implies left right) (implies right left)
instance HeytingAlgebra () where
implies _ _ = ()
instance HeytingAlgebra Bool where
implies left right =
not left || right
instance
(HeytingAlgebra left, HeytingAlgebra right) =>
HeytingAlgebra (left, right)
where
implies (leftA, rightA) (leftB, rightB) =
(implies leftA leftB, implies rightA rightB)
instance HeytingAlgebra value => HeytingAlgebra (key -> value) where
implies left right key =
implies (left key) (right key)
type BooleanAlgebra :: Type -> Constraint
class HeytingAlgebra a => BooleanAlgebra a where
complement :: a -> a
symmetricDifference :: a -> a -> a
symmetricDifference left right =
join
(meet left (complement right))
(meet (complement left) right)
instance BooleanAlgebra () where
complement _ = ()
instance BooleanAlgebra Bool where
complement =
not
instance
(BooleanAlgebra left, BooleanAlgebra right) =>
BooleanAlgebra (left, right)
where
complement (left, right) =
(complement left, complement right)
instance BooleanAlgebra value => BooleanAlgebra (key -> value) where
complement value key =
complement (value key)
iterateFixpointFrom ::
Eq a =>
Natural ->
a ->
(a -> a) ->
Either (FixpointDivergence a) a
iterateFixpointFrom budget seed step =
fixpointBounded budget step seed
{-# INLINE iterateFixpointFrom #-}
leastFixpoint ::
(Eq a, BoundedJoinSemilattice a) =>
Natural ->
(a -> a) ->
Either (FixpointDivergence a) a
leastFixpoint budget =
iterateFixpointFrom budget bottom
{-# INLINE leastFixpoint #-}
greatestFixpoint ::
(Eq a, BoundedMeetSemilattice a) =>
Natural ->
(a -> a) ->
Either (FixpointDivergence a) a
greatestFixpoint budget =
iterateFixpointFrom budget top
{-# INLINE greatestFixpoint #-}
leastPreFixpoint ::
(Eq a, BoundedJoinSemilattice a) =>
Natural ->
(a -> a) ->
Either (FixpointDivergence a) a
leastPreFixpoint budget =
leastPreFixpointFrom budget bottom
{-# INLINE leastPreFixpoint #-}
leastPreFixpointFrom ::
(Eq a, JoinSemilattice a) =>
Natural ->
a ->
(a -> a) ->
Either (FixpointDivergence a) a
leastPreFixpointFrom budget seed step =
fixpointBounded budget (\current -> join current (step current)) seed
{-# INLINE leastPreFixpointFrom #-}
greatestPostFixpoint ::
(Eq a, BoundedMeetSemilattice a) =>
Natural ->
(a -> a) ->
Either (FixpointDivergence a) a
greatestPostFixpoint budget =
greatestPostFixpointFrom budget top
{-# INLINE greatestPostFixpoint #-}
greatestPostFixpointFrom ::
(Eq a, MeetSemilattice a) =>
Natural ->
a ->
(a -> a) ->
Either (FixpointDivergence a) a
greatestPostFixpointFrom budget seed step =
fixpointBounded budget (\current -> meet current (step current)) seed
{-# INLINE greatestPostFixpointFrom #-}
joinLeq :: (Eq a, JoinSemilattice a) => a -> a -> Bool
joinLeq left right =
join left right == right
meetLeq :: (Eq a, MeetSemilattice a) => a -> a -> Bool
meetLeq left right =
meet left right == left
joins :: (BoundedJoinSemilattice a, Foldable foldable) => foldable a -> a
joins =
foldl' join bottom
joins1 :: JoinSemilattice a => NonEmpty a -> a
joins1 (first :| rest) =
foldl' join first rest
meets :: (BoundedMeetSemilattice a, Foldable foldable) => foldable a -> a
meets =
foldl' meet top
meets1 :: MeetSemilattice a => NonEmpty a -> a
meets1 (first :| rest) =
foldl' meet first rest
fromBool :: BoundedLattice a => Bool -> a
fromBool value =
if value then top else bottom