regular-0.2: src/Generics/Regular/Functions/Fold.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
-----------------------------------------------------------------------------
-- |
-- Module : Generics.Regular.Functions.Fold
-- Copyright : (c) 2008 Universiteit Utrecht
-- License : BSD3
--
-- Maintainer : generics@haskell.org
-- Stability : experimental
-- Portability : non-portable
--
-- Summary: Generic folding and unfolding.
-----------------------------------------------------------------------------
module Generics.Regular.Functions.Fold (
-- * Generic folding
Alg, Algebra,
Fold, alg,
fold,
-- * Generic unfolding
CoAlg, CoAlgebra,
Unfold, coalg,
unfold,
-- * Construction of algebras
(&)
) where
import Generics.Regular.Base
-----------------------------------------------------------------------------
-- Folds
-----------------------------------------------------------------------------
type family Alg (f :: (* -> *))
(r :: *) -- result type
:: *
-- | For a constant, we take the constant value to a result.
type instance Alg (K a) r = a -> r
-- | For a unit, no arguments are available.
type instance Alg U r = r
-- | For an identity, we turn the recursive result into a final result.
type instance Alg I r = r -> r
-- | For a sum, the algebra is a pair of two algebras.
type instance Alg (f :+: g) r = (Alg f r, Alg g r)
-- | For a product where the left hand side is a constant, we
-- take the value as an additional argument.
type instance Alg ( K a :*: g) r = a -> Alg g r
type instance Alg (S s (K a) :*: g) r = a -> Alg g r
-- | For a product where the left hand side is an identity, we
-- take the recursive result as an additional argument.
type instance Alg (I :*: g) r = r -> Alg g r
-- | Constructors are ignored.
type instance Alg (C c f) r = Alg f r
-- | Selectors are ignored.
type instance Alg (S s f) r = Alg f r
type Algebra a r = Alg (PF a) r
-- | The class fold explains how to convert an algebra
-- 'Alg' into a function from functor to result.
class Fold (f :: * -> *) where
alg :: Alg f r -> f r -> r
instance Fold (K a) where
alg f (K x) = f x
instance Fold U where
alg f U = f
instance Fold I where
alg f (I x) = f x
instance (Fold f, Fold g) => Fold (f :+: g) where
alg (f, _) (L x) = alg f x
alg (_, g) (R x) = alg g x
instance (Fold g) => Fold (K a :*: g) where
alg f (K x :*: y) = alg (f x) y
instance (Fold g) => Fold (I :*: g) where
alg f (I x :*: y) = alg (f x) y
instance (Fold f) => Fold (C c f) where
alg f (C x) = alg f x
instance (Fold f) => Fold (S s f) where
alg f (S x) = alg f x
-- | Fold with convenient algebras.
fold :: (Regular a, Fold (PF a), Functor (PF a))
=> Algebra a r -> a -> r
fold f = alg f . fmap (\x -> fold f x) . from
-----------------------------------------------------------------------------
-- Unfolds
-----------------------------------------------------------------------------
type family CoAlg (f :: (* -> *))
(s :: *) -- seed type
:: *
-- | For a constant, we produce a constant value as a result.
type instance CoAlg (K a) s = a
-- | For an identity, we produce a new seed to create the recursive result.
type instance CoAlg I s = s
-- | Units can only produce units, so we use the singleton type to encode the
-- lack of choice.
type instance CoAlg U s = ()
-- | For a sum, the coalgebra produces either the left or the right side.
type instance CoAlg (f :+: g) s = Either (CoAlg f s) (CoAlg g s)
-- | For a produt, the coalgebra is a pair of the two arms.
type instance CoAlg (f :*: g) s = (CoAlg f s, CoAlg g s)
-- | Constructors are ignored.
type instance CoAlg (C c f) s = CoAlg f s
-- | Selectors are ignored.
type instance CoAlg (S r f) s = CoAlg f s
type CoAlgebra a s = s -> CoAlg (PF a) s
-- | The class unfold explains how to convert a coalgebra 'CoAlg' and a seed
-- into a representation.
class Unfold (f :: * -> *) where
coalg :: (s -> a) -> CoAlg f s -> f a
instance Unfold (K a) where
coalg _ = K
instance Unfold I where
coalg r a = I (r a)
instance Unfold U where
coalg _ _ = U
instance (Unfold f, Unfold g) => Unfold (f :+: g) where
coalg r (Left c) = L (coalg r c)
coalg r (Right c) = R (coalg r c)
instance (Unfold f, Unfold g) => Unfold (f :*: g) where
coalg r (c, g) = coalg r c :*: coalg r g
instance Unfold f => Unfold (C c f) where
coalg r = C . coalg r
instance Unfold f => Unfold (S s f) where
coalg r = S . coalg r
unfold :: (Unfold (PF a), Regular a) => CoAlgebra a s -> s -> a
unfold a = to . coalg (unfold a) . a
-----------------------------------------------------------------------------
-- Construction of algebras
infixr 5 &
-- | For constructing algebras it is helpful to use this pairing combinator.
(&) :: a -> b -> (a, b)
(&) = (,)