group-theory-0.2.0.0: src/Data/Group/Foldable.hs
{-# language CPP #-}
{-# language FlexibleInstances #-}
{-# language Safe #-}
#if MIN_VERSION_base(4,12,0)
{-# language TypeOperators #-}
#endif
-- |
-- Module : Data.Group
-- Copyright : (c) 2020 Reed Mullanix, Emily Pillmore
-- License : BSD-style
--
-- Maintainer : Reed Mullanix <reedmullanix@gmail.com>,
-- Emily Pillmore <emilypi@cohomolo.gy>
--
-- Stability : stable
-- Portability : non-portable
--
-- This module provides definitions 'GroupFoldable',
-- along with useful combinators.
--
module Data.Group.Foldable
( -- * Group foldable
GroupFoldable(..)
-- ** Group foldable combinators
, gold
, goldr
, toFreeGroup
) where
import Data.Functor.Compose
import Data.Functor.Const
import Data.Functor.Identity
import Data.Group
import Data.Group.Free
import Data.Group.Free.Church
import Data.Group.Permutation
import Data.Monoid
#if MIN_VERSION_base(4,12,0)
import GHC.Generics
#endif
-- $setup
--
-- >>> import qualified Prelude
-- >>> import Data.Group
-- >>> import Data.Monoid
-- >>> import Data.Semigroup
-- >>> import Data.Word
-- >>> :set -XTypeApplications
-- >>> :set -XFlexibleContexts
-- -------------------------------------------------------------------- --
-- Group foldable
-- | The class of data structures that can be groupoidally folded.
--
-- 'GroupFoldable' has difficult-to-define laws in terms of Haskell,
-- but is well-understood categorically: 'GroupFoldable's are
-- functors (not necessarily 'Functor's) in the slice category \( [\mathcal{Hask}, \mathcal{Hask}] / F \),
-- where \( F \) is the free group functor. Hence, they are
-- defined by the natural transformations \( [\mathcal{Hask},\mathcal{Hask}](-, F) \) - i.e. 'toFG', or 'toFreeGroup'.
--
class GroupFoldable t where
-- | Apply a 'Group' fold to some container.
--
-- This function takes a container that can be represented as a
-- 'FreeGroup', and simplifies the container as a word in the
-- free group, producing a final output according to some
-- mapping of elements into the target group.
--
-- The name is a pun on 'Group' and 'Data.Foldable.fold'.
--
-- === __Examples__:
--
-- >>> let x = FreeGroup $ [Left (1 :: Sum Word8), Left 2, Right 2, Right 3]
-- >>> goldMap id x
-- Sum {getSum = 2}
--
-- >>> goldMap (\a -> if a < 2 then mempty else a) x
-- Sum {getSum = 3}
--
goldMap :: Group g => (a -> g) -> t a -> g
goldMap f t = runFG (toFG t) f
{-# inline goldMap #-}
-- | Translate a 'GroupFoldable' container into a Church-encoded
-- free group.
--
-- Analagous to 'Data.Foldable.toList' for 'Foldable', if 'Data.Foldable.toList' respected the
-- associativity of ⊥.
--
toFG :: t a -> FG a
toFG t = FG $ \k -> goldMap k t
{-# inline toFG #-}
{-# minimal goldMap | toFG #-}
instance GroupFoldable FG where
toFG = id
instance GroupFoldable FreeGroup where
toFG = reflectFG
instance GroupFoldable Sum where
goldMap f = f . getSum
instance GroupFoldable Product where
goldMap f = f . getProduct
instance GroupFoldable Dual where
goldMap f = f . getDual
instance GroupFoldable (Const a) where
goldMap _ _ = mempty
instance GroupFoldable Identity where
goldMap f = f . runIdentity
instance (GroupFoldable f, GroupFoldable g) => GroupFoldable (Compose f g) where
goldMap f = goldMap (goldMap f) . getCompose
#if MIN_VERSION_base(4,12,0)
instance (GroupFoldable f, GroupFoldable g) => GroupFoldable (f :*: g) where
goldMap f (a :*: b) = goldMap f a <> goldMap f b
instance (GroupFoldable f, GroupFoldable g) => GroupFoldable (f :+: g) where
toFG (L1 l) = toFG l
toFG (R1 r) = toFG r
instance (GroupFoldable f, GroupFoldable g) => GroupFoldable (f :.: g) where
goldMap f = goldMap (goldMap f) . unComp1
#endif
instance GroupFoldable Abelianizer where
goldMap _ Quot = mempty
goldMap f (Commuted a) = f a
-- -------------------------------------------------------------------- --
-- Group foldable combinators
-- | Simplify a word in 'GroupFoldable' container as a word
-- in a 'FreeGroup'.
--
-- The name is a pun on 'Group' and 'Data.Foldable.fold'.
--
-- === __Examples__:
--
-- >>> let x = FreeGroup $ [Left (1 :: Sum Word8), Left 2, Right 2, Right 3]
-- >>> gold x
-- Sum {getSum = 2}
--
gold :: (GroupFoldable t, Group g) => t g -> g
gold = goldMap id
{-# inline gold #-}
-- | Convert a 'GroupFoldable' container into a 'FreeGroup'
--
toFreeGroup :: (GroupFoldable t, Group g) => t g -> FreeGroup g
toFreeGroup = reifyFG . toFG
{-# inline toFreeGroup #-}
-- | A right group fold from a 'GroupFoldable' container to its permutation group
--
-- Analogous to 'Data.Foldable.foldr' for monoidal 'Foldable's.
--
goldr
:: GroupFoldable t
=> Group g
=> (a -> Permutation g)
-> t a
-> Permutation g
goldr = goldMap
{-# inline goldr #-}