packages feed

duoids-0.0.1.0: src/Control/Duoidal/Laws.hs

{-# LANGUAGE Safe #-}

-- |
-- Copyright: 2024 Greg Pfeil
-- License: AGPL-3.0-only WITH Universal-FOSS-exception-1.0 OR LicenseRef-commercial
module Control.Duoidal.Laws
  ( interchange,
    joinUnit,
    splitUnit,
    swapUnit,
    unswapUnit,
  )
where

import "base" Control.Arrow ((&&&))
import "base" Control.Category ((.))
import "base" Data.Eq (Eq, (==))
import "base" Data.Function (($))
import "base" Data.Tuple (uncurry)
import "this" Control.Duoidal (Duoidal, liftA2, liftM2, pure, return)
import "this" Control.Duoidal qualified as Duoidal (Normal)
import "this" Data.Duoid.Laws (Law (Law))

-- commonUnit :: forall f. Iso' (f a) (f a)
-- commonUnit = iso id id

-- data LeafyTree a = Node (LeafyTree a) (LeafyTree a) | Leaf a

-- instance Applicative (Parallel (Either (LeafyTree a))) where
--   pure = Parallel . pure
--   Left as <*> Left bs = Left (Node as bs)
--   Left as <*> Right _ = Left as
--   Right _ <*> Left bs = Left bs
--   Right f <*> Right a = Right $ f a

-- -- Semigroup a => Duoidal.validateNormal @(Either (LeafyTree a)) _ id id commonUnit
-- -- Duoidal.validateNormal @IO _ id id commonUnit
-- -- Duoidal.validateNormal @[] _ id id (iso ((: []) . head) (repeat . head))

-- |
--
-- ```
--  |        |
-- / \      / \
-- A C      A C
-- | |  ->  \_/
-- | |      / \
-- B D      B D
-- \ /      \ /
--  |        |
-- ```
interchange ::
  (Duoidal f, Eq (f ((a, c), (b, d)))) =>
  (f ((a, b), (c, d)) -> f ((a, c), (b, d))) ->
  Law (f a, f b, f c, f d) (f ((a, c), (b, d)))
interchange fn =
  Law
    (==)
    (\(fa, fb, fc, fd) -> fn . liftA2 (,) (liftM2 (,) fa fb) $ liftM2 (,) fc fd)
    $ \(fa, fb, fc, fd) -> liftM2 (,) (liftA2 (,) fa fc) $ liftA2 (,) fb fd

splitUnit :: (Duoidal f, Eq (f (a, a))) => (f a -> f (a, a)) -> Law a (f (a, a))
splitUnit fn =
  Law (==) (fn . pure) $ uncurry (liftM2 (,)) . (pure &&& pure)

joinUnit :: (Duoidal f, Eq (f a)) => (f (a, a) -> f a) -> Law a (f a)
joinUnit fn =
  Law
    (==)
    (fn . uncurry (liftA2 (,)) . (return &&& return))
    return

swapUnit :: (Duoidal f, Eq (f a)) => (f a -> f a) -> Law a (f a)
swapUnit fn = Law (==) (fn . pure) return

-- | Additional law for /normal/ duoids.
unswapUnit :: (Duoidal.Normal f, Eq (f a)) => (f a -> f a) -> Law a (f a)
unswapUnit fn = Law (==) (fn . return) pure