packages feed

moonlight-delta-0.1.0.0: src-epoch/Moonlight/Delta/Epoch/Internal/Projection.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}

-- | The context projection join-semilattice: a pair of dirty-key sets under
-- componentwise union — a commutative idempotent monoid whose components are
-- canonical persistent sets read independently by consumers.  The carrier is
-- canonical by construction, so 'normalizeContextProjectionDelta' is the
-- identity: both components have no representational slack, they inhabit
-- different key spaces, and every eliminator reads one component alone, so
-- any non-identity normalization would alter observable content for some
-- input.  The exported witnesses answer to the 'DeltaNormalize' and
-- 'DeltaSupport' contracts and are enrolled in the package law harnesses.
module Moonlight.Delta.Epoch.Internal.Projection
  ( ContextProjectionDelta (..),
    emptyContextProjectionDelta,
    dirtyBaseDelta,
    dirtyResultDelta,
    normalizeContextProjectionDelta,
    nullContextProjectionDelta,
    mapContextProjectionDelta,
  )
where

import Data.Kind (Type)
import Moonlight.Core (OrdSet (..))
import Moonlight.Delta.Normalize (DeltaNormalize (..))
import Moonlight.Delta.Support (DeltaSupport (..))
import Prelude
  ( Bool,
    Eq,
    Functor,
    Monoid (..),
    Ord,
    Semigroup (..),
    Show,
    fmap,
    id,
    (&&),
    (.),
  )

type ContextProjectionDelta :: Type -> Type
data ContextProjectionDelta observed = ContextProjectionDelta
  { dirtyBaseKeys :: !observed,
    dirtyResultKeys :: !observed
  }
  deriving stock (Eq, Ord, Show, Functor)

instance OrdSet observed => Semigroup (ContextProjectionDelta observed) where
  leftDelta <> rightDelta =
    ContextProjectionDelta
      { dirtyBaseKeys =
          unionSet (dirtyBaseKeys leftDelta) (dirtyBaseKeys rightDelta),
        dirtyResultKeys =
          unionSet (dirtyResultKeys leftDelta) (dirtyResultKeys rightDelta)
      }

instance OrdSet observed => Monoid (ContextProjectionDelta observed) where
  mempty = emptyContextProjectionDelta

emptyContextProjectionDelta :: OrdSet observed => ContextProjectionDelta observed
emptyContextProjectionDelta =
  ContextProjectionDelta
    emptySet
    emptySet

dirtyBaseDelta :: OrdSet observed => SetKey observed -> ContextProjectionDelta observed
dirtyBaseDelta key =
  emptyContextProjectionDelta
    { dirtyBaseKeys = singletonSet key
    }

dirtyResultDelta :: OrdSet observed => SetKey observed -> ContextProjectionDelta observed
dirtyResultDelta key =
  emptyContextProjectionDelta
    { dirtyResultKeys = singletonSet key
    }

normalizeContextProjectionDelta :: ContextProjectionDelta observed -> ContextProjectionDelta observed
normalizeContextProjectionDelta =
  id
{-# INLINE normalizeContextProjectionDelta #-}

nullContextProjectionDelta :: OrdSet observed => ContextProjectionDelta observed -> Bool
nullContextProjectionDelta deltaValue =
  nullSet (dirtyBaseKeys deltaValue)
    && nullSet (dirtyResultKeys deltaValue)
{-# INLINE nullContextProjectionDelta #-}

instance OrdSet observed => DeltaNormalize (ContextProjectionDelta observed) where
  normalizeDelta =
    normalizeContextProjectionDelta

  deltaNull =
    nullContextProjectionDelta

instance OrdSet observed => DeltaSupport (ContextProjectionDelta observed) where
  type DeltaSupportSet (ContextProjectionDelta observed) = ContextProjectionDelta observed

  emptySupport =
    emptyContextProjectionDelta

  deltaSupport =
    normalizeContextProjectionDelta

mapContextProjectionDelta ::
  (OrdSet observed1, OrdSet observed2) =>
  (SetKey observed1 -> SetKey observed2) ->
  ContextProjectionDelta observed1 ->
  ContextProjectionDelta observed2
mapContextProjectionDelta rekey deltaValue =
  ContextProjectionDelta
    { dirtyBaseKeys = rekeySet rekey (dirtyBaseKeys deltaValue),
      dirtyResultKeys = rekeySet rekey (dirtyResultKeys deltaValue)
    }

rekeySet ::
  (OrdSet observed1, OrdSet observed2) =>
  (SetKey observed1 -> SetKey observed2) ->
  observed1 ->
  observed2
rekeySet rekey =
  fromListSet . fmap rekey . toAscListSet