packages feed

duoids-0.0.1.0: src/Data/Duoid/Laws.hs

{-# LANGUAGE Safe #-}

-- |
-- Copyright: 2024 Greg Pfeil
-- License: AGPL-3.0-only WITH Universal-FOSS-exception-1.0 OR LicenseRef-commercial
module Data.Duoid.Laws
  ( Law (..),
    Laws (..),
    getLaws,
    NormalLaws (..),
    getNormalLaws,
    checkLaw,
    interchangeLaw,
    joinUnitLaw,
    splitUnitLaw,
    swapUnitLaw,
    unswapUnitLaw,
  )
where

import "base" Data.Bool (Bool)
import "base" Data.Kind (Type)
import "this" Data.Duoid (Duoid, pempty, sempty, (>->), (|-|))
import "this" Data.Duoid qualified as Duoid (Normal)

type Law :: Type -> Type -> Type
data Law a b = Law {cmp :: b -> b -> Bool, x :: a -> b, y :: a -> b}

checkLaw :: Law a b -> a -> Bool
checkLaw law a = cmp law (x law a) (y law a)

interchangeLaw :: (Duoid a) => (a -> a -> Bool) -> Law (a, a, a, a) a
interchangeLaw fn =
  Law
    fn
    (\(a, b, c, d) -> (a >-> b) |-| (c >-> d))
    (\(a, b, c, d) -> (a |-| c) >-> (b |-| d))

splitUnitLaw :: (Duoid a) => (a -> a -> Bool) -> Law () a
splitUnitLaw fn = Law fn (\() -> pempty) (\() -> pempty >-> pempty)

joinUnitLaw :: (Duoid a) => (a -> a -> Bool) -> Law () a
joinUnitLaw fn = Law fn (\() -> sempty |-| sempty) (\() -> sempty)

swapUnitLaw :: (Duoid a) => (a -> a -> Bool) -> Law () a
swapUnitLaw fn = Law fn (\() -> pempty) (\() -> sempty)

type Laws :: Type -> Type
data Laws a = Laws
  { splitUnit :: Law () a,
    joinUnit :: Law () a,
    swapUnit :: Law () a,
    interchange :: Law (a, a, a, a) a
  }

getLaws :: (Duoid a) => (a -> a -> Bool) -> Laws a
getLaws fn =
  Laws
    { splitUnit = splitUnitLaw fn,
      joinUnit = joinUnitLaw fn,
      swapUnit = swapUnitLaw fn,
      interchange = interchangeLaw fn
    }

-- | Additional law for `Duoid.Normal` duoids.
unswapUnitLaw :: (Duoid.Normal a) => (a -> a -> Bool) -> Law () a
unswapUnitLaw fn = Law fn (\() -> sempty) (\() -> pempty)

type NormalLaws :: Type -> Type
data NormalLaws a = NormalLaws
  { duoidLaws :: Laws a,
    unswapUnit :: Law () a
  }

getNormalLaws :: (Duoid.Normal a) => (a -> a -> Bool) -> NormalLaws a
getNormalLaws fn =
  NormalLaws
    { duoidLaws = getLaws fn,
      unswapUnit = unswapUnitLaw fn
    }