duoids-0.0.1.0: src/Data/Duoid.hs
{-# LANGUAGE Safe #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fplugin-opt=NoRecursion:ignore-methods:sconcat #-}
-- |
-- Copyright: 2024 Greg Pfeil
-- License: AGPL-3.0-only WITH Universal-FOSS-exception-1.0 OR LicenseRef-commercial
--
-- ## resources
--
-- - https://blogs.ncl.ac.uk/andreymokhov/united-monoids/
module Data.Duoid
( Duoid,
Normal,
Par (Par, getPar),
Seq (Seq, getSeq),
pempty,
sempty,
(|-|),
(>->),
Comm (Comm),
)
where
import "base" Control.Applicative (liftA2)
import "base" Control.Category ((.))
import "base" Data.Eq (Eq, (==))
import "base" Data.Foldable (Foldable)
import "base" Data.Function (const, ($))
import "base" Data.Functor (Functor)
import "base" Data.Kind (Constraint, Type)
import "base" Data.Monoid (Monoid, mempty)
import "base" Data.Ord (Ord, max, (<), (<=), (>))
import "base" Data.Ratio (Ratio, Rational, (%))
import "base" Data.Semigroup
( Semigroup,
stimes,
stimesIdempotentMonoid,
stimesMonoid,
(<>),
)
import "base" Data.Traversable (Traversable)
import "base" Data.Word (Word, Word16, Word32, Word64, Word8)
import "base" GHC.Real (infinity)
import "base" Numeric.Natural (Natural)
import "base" Text.Read (Read)
import "base" Text.Show (Show)
import "base" Prelude (Bounded, Integral, maxBound, minBound, (+))
-- | A wrapper to allow specifying a `Monoid` for the parallel (♢) component of
-- a `Duoid`.
type Par :: Type -> Type
newtype Par a = Par {getPar :: a}
deriving stock (Eq, Ord, Read, Show, Functor, Foldable, Traversable)
-- | A wrapper to allow specifying a `Monoid` for the sequential (★) component
-- of a `Duoid`.
type Seq :: Type -> Type
newtype Seq a = Seq {getSeq :: a}
deriving stock (Eq, Ord, Read, Show, Functor, Foldable, Traversable)
-- | Instances for `Duoid` are automatically coalesced from the respective
-- @`Monoid` `.` `Par`@ and @`Monoid` `.` `Seq`@ instances.
type Duoid :: Type -> Constraint
class (Monoid (Par a), Monoid (Seq a)) => Duoid a
-- | The automatic `Duoid` instance. This is overlappable in case it doesn’t
-- suffice in specific cases.
instance {-# OVERLAPPABLE #-} (Monoid (Par a), Monoid (Seq a)) => Duoid a
-- | A duoid where there is a natural transformation between the parallel and
-- sequential units. In this category, that is when the units are identical.
type Normal :: Type -> Constraint
class (Duoid a) => Normal a
-- | The parallel unit of a `Duoid`
pempty :: (Duoid a) => a
pempty = getPar mempty
-- | The sequential unit of a `Duoid`.
sempty :: (Duoid a) => a
sempty = getSeq mempty
-- | The parallel operation of a `Duoid`.
(|-|) :: (Duoid a) => a -> a -> a
x |-| y = getPar (Par x <> Par y)
-- | The sequential operation of a `Duoid`.
(>->) :: (Duoid a) => a -> a -> a
x >-> y = getSeq (Seq x <> Seq y)
-- | A commutative `Monoid` forms a `Duoid` with itself.
--
-- __NB__: Be careful not to wrap a non-commutative `Monoid` with this newtype.
type Comm :: Type -> Type
newtype Comm a = Comm a
deriving stock (Eq, Ord, Read, Show, Functor, Foldable, Traversable)
instance (Monoid a) => Semigroup (Par (Comm a)) where
Par (Comm x) <> Par (Comm y) = Par . Comm $ x <> y
stimes = stimesMonoid
instance (Monoid a) => Monoid (Par (Comm a)) where
mempty = Par $ Comm mempty
instance (Monoid a) => Semigroup (Seq (Comm a)) where
Seq (Comm x) <> Seq (Comm y) = Seq . Comm $ x <> y
stimes = stimesMonoid
instance (Monoid a) => Monoid (Seq (Comm a)) where
mempty = Seq $ Comm mempty
-- max-plus duoids
-- | Maximum.
instance {-# OVERLAPPABLE #-} (Bounded a, Integral a) => Semigroup (Par a) where
Par x <> Par y = Par $ max x y
stimes = stimesIdempotentMonoid
-- | Maximum.
instance {-# OVERLAPPABLE #-} (Bounded a, Integral a) => Monoid (Par a) where
mempty = Par minBound
-- | Saturating addition.
instance {-# OVERLAPPABLE #-} (Bounded a, Integral a) => Semigroup (Seq a) where
Seq x <> Seq y =
let z = x + y
in Seq $
if y < 0
then
if z > x
then minBound
else z
else
if z < x
then maxBound
else z
stimes = stimesMonoid
-- | Saturating addition.
instance {-# OVERLAPPABLE #-} (Bounded a, Integral a) => Monoid (Seq a) where
mempty = Seq 0
-- | Maximum.
instance
{-# OVERLAPPABLE #-}
(Bounded a, Integral a) =>
Semigroup (Par (Ratio a))
where
Par x <> Par y = Par $ max x y
stimes = stimesIdempotentMonoid
-- | Maximum.
instance
{-# OVERLAPPABLE #-}
(Bounded a, Integral a) =>
Monoid (Par (Ratio a))
where
mempty = Par . (minBound %) $ if (minBound :: a) <= 0 then 1 else maxBound
-- | Saturating addition.
instance
{-# OVERLAPPABLE #-}
(Bounded a, Integral a) =>
Semigroup (Seq (Ratio a))
where
Seq x <> Seq y =
let z = x + y
in Seq $
if y < 0
then
if z > x
then minBound % 1
else z
else
if z < x
then maxBound % 1
else z
stimes = stimesMonoid
-- | Saturating addition.
instance
{-# OVERLAPPABLE #-}
(Bounded a, Integral a) =>
Monoid (Seq (Ratio a))
where
mempty = Seq 0
instance Normal (Ratio Word)
instance Normal (Ratio Word16)
instance Normal (Ratio Word32)
instance Normal (Ratio Word64)
instance Normal (Ratio Word8)
-- | Maximum.
instance Semigroup (Par (Ratio Natural)) where
Par x <> Par y = Par $ max x y
stimes = stimesIdempotentMonoid
-- | Maximum.
instance Monoid (Par (Ratio Natural)) where
mempty = Par 0
-- | Saturating addition.
instance Semigroup (Seq (Ratio Natural)) where
Seq x <> Seq y = Seq $ x + y
stimes = stimesMonoid
-- | Saturating addition.
instance Monoid (Seq (Ratio Natural)) where
mempty = Seq 0
-- | Maximum.
instance Semigroup (Par Rational) where
Par x <> Par y = Par $ max x y
stimes = stimesIdempotentMonoid
-- | Maximum.
instance Monoid (Par Rational) where
mempty = Par (-infinity)
-- | Saturating addition.
instance Semigroup (Seq Rational) where
Seq x <> Seq y =
Seq $
if x == (-infinity)
then
if y == infinity
then 0
else -infinity
else
if x == infinity
then
if y == (-infinity)
then 0
else infinity
else
if y == (-infinity)
then -infinity
else
if y == infinity
then infinity
else x + y
stimes = stimesMonoid
-- | Saturating addition.
instance Monoid (Seq Rational) where
mempty = Seq 0
-- | This is the max-plus duoid
instance Duoid Rational
instance Semigroup (Par Natural) where
Par x <> Par y = Par $ max x y
stimes = stimesIdempotentMonoid
instance Monoid (Par Natural) where
mempty = Par 0
instance Semigroup (Seq Natural) where
Seq x <> Seq y = Seq $ x + y
stimes = stimesMonoid
instance Monoid (Seq Natural) where
mempty = Seq 0
-- | This is the max-plus duoid.
instance Normal Natural
-- | This is the max-plus duoid.
instance Normal (Ratio Natural)
-- | This is the max-plus duoid.
instance Normal Word
-- | This is the max-plus duoid.
instance Normal Word16
-- | This is the max-plus duoid.
instance Normal Word32
-- | This is the max-plus duoid.
instance Normal Word64
-- | This is the max-plus duoid.
instance Normal Word8
-- tuples
instance Semigroup (Par ()) where
Par () <> Par () = Par ()
stimes = stimesIdempotentMonoid
instance Monoid (Par ()) where
mempty = Par ()
instance Semigroup (Seq ()) where
Seq () <> Seq () = Seq ()
stimes = stimesIdempotentMonoid
instance Monoid (Seq ()) where
mempty = Seq ()
instance Normal ()
instance (Duoid a, Duoid b) => Semigroup (Par (a, b)) where
Par (a, b) <> Par (a', b') = Par (a |-| a', b |-| b')
stimes = stimesMonoid
instance (Duoid a, Duoid b) => Monoid (Par (a, b)) where
mempty = Par (pempty, pempty)
instance (Duoid a, Duoid b) => Semigroup (Seq (a, b)) where
Seq (a, b) <> Seq (a', b') = Seq (a >-> a', b >-> b')
stimes = stimesMonoid
instance (Duoid a, Duoid b) => Monoid (Seq (a, b)) where
mempty = Seq (sempty, sempty)
instance (Normal a, Normal b) => Normal (a, b)
instance (Duoid a, Duoid b, Duoid c) => Semigroup (Par (a, b, c)) where
Par (a, b, c) <> Par (a', b', c') = Par (a |-| a', b |-| b', c |-| c')
stimes = stimesMonoid
instance (Duoid a, Duoid b, Duoid c) => Monoid (Par (a, b, c)) where
mempty = Par (pempty, pempty, pempty)
instance (Duoid a, Duoid b, Duoid c) => Semigroup (Seq (a, b, c)) where
Seq (a, b, c) <> Seq (a', b', c') = Seq (a >-> a', b >-> b', c >-> c')
stimes = stimesMonoid
instance (Duoid a, Duoid b, Duoid c) => Monoid (Seq (a, b, c)) where
mempty = Seq (sempty, sempty, sempty)
instance (Normal a, Normal b, Normal c) => Normal (a, b, c)
instance
(Duoid a, Duoid b, Duoid c, Duoid d) =>
Semigroup (Par (a, b, c, d))
where
Par (a, b, c, d) <> Par (a', b', c', d') =
Par (a |-| a', b |-| b', c |-| c', d |-| d')
stimes = stimesMonoid
instance (Duoid a, Duoid b, Duoid c, Duoid d) => Monoid (Par (a, b, c, d)) where
mempty = Par (pempty, pempty, pempty, pempty)
instance
(Duoid a, Duoid b, Duoid c, Duoid d) =>
Semigroup (Seq (a, b, c, d))
where
Seq (a, b, c, d) <> Seq (a', b', c', d') =
Seq (a >-> a', b >-> b', c >-> c', d >-> d')
stimes = stimesMonoid
instance (Duoid a, Duoid b, Duoid c, Duoid d) => Monoid (Seq (a, b, c, d)) where
mempty = Seq (sempty, sempty, sempty, sempty)
instance (Normal a, Normal b, Normal c, Duoid d) => Normal (a, b, c, d)
-- functions
instance (Duoid b) => Semigroup (Par (a -> b)) where
Par f <> Par g = Par $ liftA2 (|-|) f g
stimes = stimesMonoid
instance (Duoid b) => Monoid (Par (a -> b)) where
mempty = Par $ const pempty
instance (Duoid b) => Semigroup (Seq (a -> b)) where
Seq f <> Seq g = Seq $ liftA2 (>->) f g
stimes = stimesMonoid
instance (Duoid b) => Monoid (Seq (a -> b)) where
mempty = Seq $ const sempty
instance (Normal b) => Normal (a -> b)