packages feed

moonlight-algebra-0.1.0.0: src-abstract/Moonlight/Algebra/Pure/Product.hs

{-# LANGUAGE RoleAnnotations #-}

-- | Finite @n@-fold product algebras ('ProductAlgebra', arity at the type level)
-- with lattice, ring and module structure defined coordinatewise.
--
-- Laws: every law holds in the product exactly when it holds in each component.
module Moonlight.Algebra.Pure.Product
  ( ProductAlgebra,
    mkProductAlgebra,
    toProductList,
  )
where

import Data.Kind (Type)
import Data.List (genericLength, genericReplicate)
import Data.Proxy (Proxy (..))
import GHC.TypeNats (KnownNat, Nat, natVal)
import Numeric.Natural (Natural)
import Moonlight.Algebra.Pure.Lattice
  ( BoundedJoinSemilattice (..),
    BoundedMeetSemilattice (..),
    BooleanAlgebra (..),
    DistributiveLattice,
    HeytingAlgebra (implies),
    JoinSemilattice (..),
    Lattice,
    MeetSemilattice (..),
    OrderedLattice,
  )
import Moonlight.Algebra.Pure.Module (BilinearSpace (..), Module (..), VectorSpace)
import Moonlight.Algebra.Pure.Ring
  ( CommutativeRing,
    Semiring,
  )
import Moonlight.Core
  ( AdditiveGroup (..),
    AdditiveMonoid (..),
    Field,
    MultiplicativeMonoid (..),
    PartialOrder (..),
    Ring,
  )

type ProductAlgebra :: Nat -> Type -> Type
type role ProductAlgebra nominal representational
newtype ProductAlgebra (n :: Nat) a = ProductAlgebra [a]
  deriving stock (Eq, Show, Functor)

mkProductAlgebra :: forall n a. KnownNat n => [a] -> Maybe (ProductAlgebra n a)
mkProductAlgebra values
  | genericLength values == expectedArity (Proxy @n) = Just (ProductAlgebra values)
  | otherwise = Nothing

toProductList :: ProductAlgebra n a -> [a]
toProductList (ProductAlgebra values) = values

liftProduct2 :: (a -> b -> c) -> ProductAlgebra n a -> ProductAlgebra n b -> ProductAlgebra n c
liftProduct2 combine (ProductAlgebra left) (ProductAlgebra right) =
  ProductAlgebra (zipWith combine left right)

expectedArity :: forall n. KnownNat n => Proxy n -> Natural
expectedArity _ = natVal (Proxy @n)

instance KnownNat n => Applicative (ProductAlgebra n) where
  pure value = ProductAlgebra (genericReplicate (expectedArity (Proxy @n)) value)
  ProductAlgebra transforms <*> ProductAlgebra values =
    ProductAlgebra (zipWith ($) transforms values)

instance (KnownNat n, AdditiveMonoid a) => AdditiveMonoid (ProductAlgebra n a) where
  zero = pure zero
  add = liftProduct2 add

instance (KnownNat n, AdditiveGroup a) => AdditiveGroup (ProductAlgebra n a) where
  neg = fmap neg
  sub left right = add left (neg right)

instance (KnownNat n, MultiplicativeMonoid a) => MultiplicativeMonoid (ProductAlgebra n a) where
  one = pure one
  mul = liftProduct2 mul

instance (KnownNat n, Ring a) => Ring (ProductAlgebra n a)

instance (KnownNat n, Semiring a) => Semiring (ProductAlgebra n a)

instance (KnownNat n, CommutativeRing a) => CommutativeRing (ProductAlgebra n a)

instance (KnownNat n, Ring r) => Module r (ProductAlgebra n r) where
  scale scalar = fmap (mul scalar)

instance (KnownNat n, Field k) => VectorSpace k (ProductAlgebra n k)

instance (KnownNat n, Field k) => BilinearSpace k (ProductAlgebra n k) where
  bilinearForm (ProductAlgebra left) (ProductAlgebra right) =
    foldr add zero (zipWith mul left right)

instance JoinSemilattice a => JoinSemilattice (ProductAlgebra n a) where
  join = liftProduct2 join

instance (KnownNat n, BoundedJoinSemilattice a) => BoundedJoinSemilattice (ProductAlgebra n a) where
  bottom = pure bottom

instance MeetSemilattice a => MeetSemilattice (ProductAlgebra n a) where
  meet = liftProduct2 meet

instance PartialOrder a => PartialOrder (ProductAlgebra n a) where
  leq (ProductAlgebra left) (ProductAlgebra right) =
    and (zipWith leq left right)

instance (KnownNat n, BoundedMeetSemilattice a) => BoundedMeetSemilattice (ProductAlgebra n a) where
  top = pure top

instance Lattice a => Lattice (ProductAlgebra n a)

instance OrderedLattice a => OrderedLattice (ProductAlgebra n a)

instance DistributiveLattice a => DistributiveLattice (ProductAlgebra n a)

instance (KnownNat n, HeytingAlgebra a) => HeytingAlgebra (ProductAlgebra n a) where
  implies = liftProduct2 implies

instance (KnownNat n, BooleanAlgebra a) => BooleanAlgebra (ProductAlgebra n a) where
  complement = fmap complement