group-theory-0.2.0.0: src/Data/Group/Free/Church.hs
{-# language RankNTypes #-}
{-# language Safe #-}
-- |
-- 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 for Church-encoded
-- 'FreeGroup's, 'Data.Group.Free.Internal.FreeAbelianGroup's, along with useful combinators.
--
module Data.Group.Free.Church
( -- * Church-encoded free groups
FG(..)
-- ** Church-encoded free group combinators
, interpretFG
, reifyFG
, reflectFG
, presentFG
-- * Church-encoded free abelian groups
, FA(..)
-- ** Church-encoded free abelian group combinators
, forgetFA
, interpretFA
, reifyFA
, reflectFA
) where
import Control.Applicative
import Control.Monad
import Data.Semigroup(Semigroup(..))
import Data.Group
import Data.Group.Free
import qualified Data.Map.Strict as Map
-- | The Church-encoding of a 'FreeGroup'.
--
-- This datatype represents the "true" free group in Haskell on some @a@-valued
-- generators. For more information on why this encoding is preferred,
-- see Dan Doel's <http://comonad.com/reader/2015/free-monoids-in-haskell/ article> in
-- the Comonad Reader.
--
-- While 'FreeGroup' et al are free in a strict language, and are more intuitive,
-- they are not associative wtih respect to bottoms. 'FG' and 'FA' however, are,
-- and should be preferred when working with possibly undefined data.
--
newtype FG a = FG { runFG :: forall g. Group g => (a -> g) -> g }
instance Semigroup (FG a) where
(FG g) <> (FG g') = FG $ \k -> g k <> g' k
instance Monoid (FG a) where
mempty = FG $ const mempty
instance Group (FG a) where
invert (FG g) = FG (invert . g)
instance Functor FG where
fmap f (FG fa) = FG $ \k -> fa (k . f)
instance Applicative FG where
pure a = FG ($ a)
(<*>) = ap
instance Monad FG where
return = pure
(FG fg) >>= f = FG $ \k -> fg (\a -> (runFG $ f a) k)
instance Alternative FG where
empty = mempty
(<|>) = (<>)
-- | Interpret a Church-encoded free group as a concrete 'FreeGroup'.
--
interpretFG :: Group g => FG g -> g
interpretFG (FG fg) = fg id
{-# inline interpretFG #-}
-- | Convert a Church-encoded free group to a concrete 'FreeGroup'.
--
reifyFG :: FG a -> FreeGroup a
reifyFG fg = interpretFG $ fmap pure fg
{-# inline reifyFG #-}
-- | Convert a concrete 'FreeGroup' to a Church-encoded free group.
--
reflectFG :: FreeGroup a -> FG a
reflectFG (FreeGroup fg) = FG $ \k -> foldMap (go k) fg
where
go k (Left a) = invert (k a)
go k (Right a) = k a
{-# inline reflectFG #-}
-- | Present a 'Group' as a 'FG' modulo relations.
--
presentFG :: Group g => FG g -> (FG g -> g) -> g
presentFG = flip ($)
{-# inline presentFG #-}
----------------------------------------
-- Free Abelian Groups
-- | The Church-encoding of a 'Data.Group.Free.Internal.FreeAbelianGroup'.
--
-- This datatype represents the free group on some @a@-valued
-- generators, along with their exponents in the group.
--
newtype FA a = FA { runFA :: forall g. Abelian g => (a -> Integer -> g) -> g }
instance Semigroup (FA a) where
(FA g) <> (FA g') = FA $ \k -> g k <> g' k
stimes = gtimes
instance Monoid (FA a) where
mempty = FA $ const mempty
instance Group (FA a) where
invert g = pow g (-1 :: Integer)
{-
Note: This implementation "optimizes" from the default implementation of
'pow', or more natural
> pow (FA g) n = FA $ \k -> gtimes n (g k)
by delaying the call of 'gtimes' as late as possible.
This is only possible because we expect 'Group g' to be an abelian group,
which implies the following equation hold:
> pow (x <> y) n = pow x n <> pow y n
-}
pow (FA g) n
| n == 0 = mempty
| otherwise = FA $ \k -> g (\a i -> k a (toInteger n * i))
instance Abelian (FA a)
instance Functor FA where
fmap f (FA fa) = FA $ \k -> fa (k . f)
instance Applicative FA where
pure a = FA $ \k -> k a 1
(<*>) = ap
instance Monad FA where
return = pure
fa >>= f = interpretFA $ fmap f fa
instance Alternative FA where
empty = mempty
(<|>) = (<>)
-- | Interpret a Church-encoded free abelian group as a concrete 'Data.Group.Free.Internal.FreeAbelianGroup'.
--
interpretFA :: Abelian g => FA g -> g
interpretFA (FA fa) = fa pow
{-# inline interpretFA #-}
-- | Convert a Church-encoded free abelian group to a concrete 'Data.Group.Free.Internal.FreeAbelianGroup'.
--
reifyFA :: Ord a => FA a -> FreeAbelianGroup a
reifyFA = interpretFA . fmap singleton
{-# inline reifyFA #-}
-- | Convert a concrete 'Data.Group.Free.Internal.FreeAbelianGroup' to a Church-encoded free abelian group.
--
reflectFA :: FreeAbelianGroup a -> FA a
reflectFA fa =
let g = runFreeAbelianGroup fa
in FA $ \k -> Map.foldMapWithKey k g
{-# inline reflectFA #-}
-- | Forget the commutative structure of a Church-encoded free abelian group,
-- turning it into a standard free group.
forgetFA :: (Ord a) => FA a -> FG a
forgetFA fa = case reifyFA fa of
~(FreeAbelianGroup fa') -> FG $ \t -> Map.foldMapWithKey (\a n -> t a `pow` n) fa'
{-# inline forgetFA #-}