packages feed

moonlight-algebra-0.1.0.0: src-finite-lattice/Moonlight/FiniteLattice/Core.hs

{-# LANGUAGE GHC2024 #-}

-- | The 'ContextLattice' carrier, built by checked compilation from a declared
-- finite order, and the value-level 'joinContext', 'meetContext', 'leqContext'
-- queries against it.
module Moonlight.FiniteLattice.Core
  ( ContextLattice,
    clTop,
    clBottom,
    ContextOrderDecl (..),
    ContextCompileLimits (..),
    defaultContextCompileLimits,
    unlimitedContextCompileLimits,
    ContextRepresentation (..),
    ContextLatticeCompileError (..),
    ContextLatticeLookupError (..),
    contextOrderDecl,
    compileContextLattice,
    compileContextLatticeWith,
    contextLatticeFromClosedOrder,
    contextLatticeFromClosedOrderWith,
    singletonContextLattice,
    latticeContext,
    orderedLatticeContext,
    contextLatticeSize,
    contextLatticeElements,
    contextMember,
    joinContext,
    meetContext,
    leqContext,
  )
where

import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Data.Vector qualified as Vector
import Moonlight.FiniteLattice.Internal.Compile
  ( compileContextLattice,
    compileContextLatticeWith,
    contextLatticeFromClosedOrder,
    contextLatticeFromClosedOrderWith,
    singletonContextLattice,
  )
import Moonlight.FiniteLattice.Internal.Key (ContextKey)
import Moonlight.FiniteLattice.Internal.Plan
  ( contextPlanJoinKey,
    contextPlanLeq,
    contextPlanMeetKey,
  )
import Moonlight.FiniteLattice.Internal.Types
  ( ContextLattice (..),
    ContextCompileLimits (..),
    ContextLatticeCompileError (..),
    ContextLatticeLookupError (..),
    ContextOrderDecl (..),
    ContextRepresentation (..),
    contextKeyForMaybe,
    contextValueForKey,
    defaultContextCompileLimits,
    unlimitedContextCompileLimits,
  )
import Moonlight.Algebra.Pure.Lattice
  ( BoundedJoinSemilattice (bottom),
    BoundedMeetSemilattice (top),
    JoinSemilattice (join),
    Lattice,
    MeetSemilattice (meet),
    OrderedLattice,
  )
import Moonlight.Core
  ( FiniteUniverse,
    finiteUniverseList,
    leq,
  )

contextOrderDecl ::
  Ord c =>
  c ->
  c ->
  [(c, c)] ->
  ContextOrderDecl c
contextOrderDecl topValue bottomValue generatingPairs =
  ContextOrderDecl
    { codTop = topValue,
      codBottom = bottomValue,
      codGeneratingPairs = Set.fromList generatingPairs
    }

contextLatticeSize :: ContextLattice c -> Int
contextLatticeSize = clSize
{-# INLINE contextLatticeSize #-}

contextLatticeElements :: ContextLattice c -> [c]
contextLatticeElements =
  Vector.toList . clContextsByKey

contextMember :: Ord c => ContextLattice c -> c -> Bool
contextMember lattice =
  (`Map.member` clKeyByContext lattice)

-- | Value-level boundary query; repeated validation is a performance tax, so descend once into 'Moonlight.FiniteLattice.Resident' for steady-state algebra.
joinContext ::
  Ord c =>
  ContextLattice c ->
  c ->
  c ->
  Either (ContextLatticeLookupError c) c
joinContext lattice leftContext rightContext = do
  leftKey <- lookupContextKey lattice leftContext
  rightKey <- lookupContextKey lattice rightContext
  let joinedKey = contextPlanJoinKey (clPlan lattice) leftKey rightKey
  pure
    ( contextValueForKey
        lattice
        joinedKey
    )

meetContext ::
  Ord c =>
  ContextLattice c ->
  c ->
  c ->
  Either (ContextLatticeLookupError c) c
meetContext lattice leftContext rightContext = do
  leftKey <- lookupContextKey lattice leftContext
  rightKey <- lookupContextKey lattice rightContext
  let metKey = contextPlanMeetKey (clPlan lattice) leftKey rightKey
  pure
    ( contextValueForKey
        lattice
        metKey
    )

leqContext ::
  Ord c =>
  ContextLattice c ->
  c ->
  c ->
  Either (ContextLatticeLookupError c) Bool
leqContext lattice leftContext rightContext = do
  leftKey <- lookupContextKey lattice leftContext
  rightKey <- lookupContextKey lattice rightContext
  pure (contextPlanLeq (clPlan lattice) leftKey rightKey)

lookupContextKey ::
  Ord c =>
  ContextLattice c ->
  c ->
  Either (ContextLatticeLookupError c) ContextKey
lookupContextKey lattice contextValue =
  maybe
    (Left (ContextLatticeUnknownContext contextValue))
    Right
    (contextKeyForMaybe lattice contextValue)

-- | Checked construction from the declared finite instances. This deliberately
-- returns 'Either': typeclass laws are promises, not runtime evidence.
latticeContext ::
  ( Ord c,
    Lattice c,
    BoundedJoinSemilattice c,
    BoundedMeetSemilattice c,
    Enum c,
    Bounded c
  ) =>
  Either (ContextLatticeCompileError c) (ContextLattice c)
latticeContext =
  contextLatticeFromClosedOrder
    top
    bottom
    [minBound .. maxBound]
    (\leftContext rightContext -> join leftContext rightContext == rightContext)
    join
    meet

orderedLatticeContext ::
  ( Ord c,
    OrderedLattice c,
    BoundedJoinSemilattice c,
    BoundedMeetSemilattice c,
    FiniteUniverse c
  ) =>
  Either (ContextLatticeCompileError c) (ContextLattice c)
orderedLatticeContext =
  contextLatticeFromClosedOrder
    top
    bottom
    finiteUniverseList
    leq
    join
    meet