{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
module Data.Algebra.Free
(
FreeAlgebra (..)
, Proof (..)
,
AlgebraType
, AlgebraType0
, unFoldMapFree
, foldFree
, natFree
, fmapFree
, joinFree
, bindFree
, cataFree
, foldrFree
, foldrFree'
, foldlFree
, foldlFree'
, Free (..)
, DNonEmpty (..)
)
where
import Prelude
#if __GLASGOW_HASKELL__ < 808
import Data.DList (DList)
#endif
import Data.DList as DList
import Data.Functor.Identity (Identity (..))
import Data.Fix (Fix, cata)
import Data.Group (Group (..))
import Data.Kind (Constraint, Type)
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NonEmpty
import Data.Monoid ( Endo (..)
#if __GLASGOW_HASKELL__ < 808
, Monoid (..)
#endif
, Dual (..))
#if __GLASGOW_HASKELL__ < 808
import Data.Semigroup ( Semigroup
, (<>)
)
#endif
import Data.Algebra.Pointed (Pointed (..))
type family AlgebraType (f :: k) (a :: l) :: Constraint
type family AlgebraType0 (f :: k) (a :: l) :: Constraint
data Proof (c :: Constraint) (a :: l) where
Proof :: c => Proof c a
class FreeAlgebra (m :: Type -> Type) where
{-# MINIMAL returnFree, foldMapFree #-}
returnFree :: a -> m a
foldMapFree
:: forall d a
. ( AlgebraType m d
, AlgebraType0 m a
)
=> (a -> d)
-> (m a -> d)
codom :: forall a. AlgebraType0 m a => Proof (AlgebraType m (m a)) (m a)
default codom :: forall a. AlgebraType m (m a)
=> Proof (AlgebraType m (m a)) (m a)
codom = Proof
forget :: forall a. AlgebraType m a => Proof (AlgebraType0 m a) (m a)
default forget :: forall a. AlgebraType0 m a
=> Proof (AlgebraType0 m a) (m a)
forget = Proof
unFoldMapFree
:: FreeAlgebra m
=> (m a -> d)
-> (a -> d)
unFoldMapFree f = f . returnFree
{-# INLINABLE unFoldMapFree #-}
foldFree
:: forall m a .
( FreeAlgebra m
, AlgebraType m a
)
=> m a
-> a
foldFree ma = case forget @m @a of
Proof -> foldMapFree id ma
{-# INLINABLE foldFree #-}
natFree :: forall m n a .
( FreeAlgebra m
, FreeAlgebra n
, AlgebraType0 m a
, AlgebraType m (n a)
)
=> m a
-> n a
natFree = foldMapFree returnFree
{-# INLINABLE natFree #-}
fmapFree :: forall m a b .
( FreeAlgebra m
, AlgebraType0 m a
, AlgebraType0 m b
)
=> (a -> b)
-> m a
-> m b
fmapFree f ma = case codom @m @b of
Proof -> foldMapFree (returnFree . f) ma
{-# INLINABLE fmapFree #-}
joinFree :: forall m a .
( FreeAlgebra m
, AlgebraType0 m a
)
=> m (m a)
-> m a
joinFree mma = case codom @m @a of
Proof -> foldFree mma
{-# INLINABLE joinFree #-}
bindFree :: forall m a b .
( FreeAlgebra m
, AlgebraType0 m a
, AlgebraType0 m b
)
=> m a
-> (a -> m b)
-> m b
bindFree ma f = case codom @m @b of
Proof -> foldMapFree f ma
{-# INLINABLE bindFree #-}
cataFree :: ( FreeAlgebra m
, AlgebraType m a
, Functor m
)
=> Fix m
-> a
cataFree = cata foldFree
foldrFree
:: forall m a b .
( FreeAlgebra m
, AlgebraType m (Endo b)
, AlgebraType0 m a
)
=> (a -> b -> b)
-> b
-> m a
-> b
foldrFree f z t = appEndo (foldMapFree (Endo . f) t) z
foldrFree'
:: forall m a b .
( FreeAlgebra m
, AlgebraType m (Dual (Endo (b -> b)))
, AlgebraType0 m a
)
=> (a -> b -> b)
-> m a
-> b
-> b
foldrFree' f xs z0 = foldlFree f' id xs z0
where
f' k x z = k $! f x z
foldlFree
:: forall m a b .
( FreeAlgebra m
, AlgebraType m (Dual (Endo b))
, AlgebraType0 m a
)
=> (b -> a -> b)
-> b
-> m a
-> b
foldlFree f z t = appEndo (getDual (foldMapFree (Dual . Endo . flip f) t)) z
foldlFree'
:: forall m a b .
( FreeAlgebra m
, AlgebraType m (Endo (b -> b))
, AlgebraType0 m a
)
=> (b -> a -> b)
-> b
-> m a
-> b
foldlFree' f z0 xs = foldrFree f' id xs z0
where
f' x k z = k $! f z x
type instance AlgebraType0 Identity a = ()
type instance AlgebraType Identity a = ()
instance FreeAlgebra Identity where
returnFree = Identity
foldMapFree f = f . runIdentity
type instance AlgebraType0 NonEmpty a = ()
type instance AlgebraType NonEmpty m = Semigroup m
instance FreeAlgebra NonEmpty where
returnFree a = a :| []
foldMapFree f (a :| []) = f a
foldMapFree f (a :| (b : bs)) = f a <> foldMapFree f (b :| bs)
newtype DNonEmpty a = DNonEmpty ([a] -> NonEmpty a)
instance Semigroup (DNonEmpty a) where
DNonEmpty f <> DNonEmpty g = DNonEmpty (f . NonEmpty.toList . g)
type instance AlgebraType0 DNonEmpty a = ()
type instance AlgebraType DNonEmpty m = Semigroup m
instance FreeAlgebra DNonEmpty where
returnFree a = DNonEmpty (a :|)
foldMapFree f (DNonEmpty g) = foldMapFree f (g [])
type instance AlgebraType0 [] a = ()
type instance AlgebraType [] m = Monoid m
instance FreeAlgebra [] where
returnFree a = [a]
foldMapFree = foldMap
type instance AlgebraType0 Maybe a = ()
type instance AlgebraType Maybe m = Pointed m
instance FreeAlgebra Maybe where
returnFree = Just
foldMapFree _ Nothing = point
foldMapFree f (Just a) = f a
newtype Free (c :: Type -> Constraint) a = Free {
runFree :: forall r. c r => (a -> r) -> r
}
instance Semigroup (Free Semigroup a) where
Free f <> Free g = Free $ \k -> f k <> g k
type instance AlgebraType0 (Free Semigroup) a = ()
type instance AlgebraType (Free Semigroup) a = Semigroup a
instance FreeAlgebra (Free Semigroup) where
returnFree a = Free $ \k -> k a
foldMapFree f (Free k) = k f
instance Semigroup (Free Monoid a) where
Free f <> Free g = Free $ \k -> f k `mappend` g k
instance Monoid (Free Monoid a) where
mempty = Free (const mempty)
#if __GLASGOW_HASKELL__ <= 802
mappend = (<>)
#endif
type instance AlgebraType0 (Free Monoid) a = ()
type instance AlgebraType (Free Monoid) a = Monoid a
instance FreeAlgebra (Free Monoid) where
returnFree a = Free $ \k -> k a
foldMapFree f (Free k) = k f
type instance AlgebraType0 DList a = ()
type instance AlgebraType DList a = Monoid a
instance FreeAlgebra DList where
returnFree = DList.singleton
foldMapFree = foldMap
instance Semigroup (Free Group a) where
Free f <> Free g = Free $ \k -> f k `mappend` g k
instance Monoid (Free Group a) where
mempty = Free (const mempty)
#if __GLASGOW_HASKELL__ <= 802
mappend = (<>)
#endif
instance Group (Free Group a) where
invert (Free k) = Free (k . invert)
type instance AlgebraType0 (Free Group) a = ()
type instance AlgebraType (Free Group) a = Group a
instance FreeAlgebra (Free Group) where
returnFree a = Free $ \k -> k a
foldMapFree f (Free k) = k f