{-# LANGUAGE TypeFamilies #-} {- | Module : Data.FixFile.Fixed Copyright : (C) 2016 Rev. Johnny Healey License : LGPL-3 Maintainer : Rev. Johnny Healey <rev.null@gmail.com> Stability : experimental Portability : unknown This is a data type that can be used with a 'FixFile' to store a set of 'Ordered' items as an unbalanced binary tree. This file is not recommended for use, but exists for educational purposes. It has a simple implementation that is easier to read than some of the more advanced balanced data types. -} module Data.FixFile.Fixed ( Fix(..) ,Fixed(..) ,CataAlg ,CataMAlg ,cata ,cataM ,AnaAlg ,AnaMAlg ,ana ,anaM ,ParaAlg ,ParaMAlg ,para ,paraM ,iso ,hylo ,hyloM ,FixedAlg(..) ,FixedSub(..) ,FixedFunctor(..) ,fmapF' ,FixedFoldable(..) ,FixedTraversable(..) ,traverseF' ) where import Control.Monad {-| 'Fixed' is a typeclass for representing the fixed point of a 'Functor'. A well-behaved instance of 'Fixed' should not change the shape of the underlying 'Functor'. In other words, the following should always be true: @ 'outf' ('inf' x) == x @ -} class Fixed g where inf :: f (g f) -> g f outf :: g f -> f (g f) {-| 'Fix' is a type for creating an in-memory representation of the fixed point of a 'Functor'. -} newtype Fix f = InF { outF :: f (Fix f) } instance Fixed Fix where inf = InF {-# INLINE inf #-} outf = outF {-# INLINE outf #-} {-| 'AnaAlg' is an anamorpism F-Algebra. -} type AnaAlg f a = a -> f a {-| 'AnaMAlg' is a monadic anamorpism F-Algebra. -} type AnaMAlg m f a = a -> m (f a) {-| 'ana' applies an AnaAlg over an argument to produce a fixed-point of a Functor. -} ana :: (Functor f, Fixed g) => AnaAlg f a -> a -> g f ana f = inf . fmap (ana f) . f {-| 'anaM' is a monadic anamorphism. -} anaM :: (Traversable f, Fixed g, Monad m) => AnaMAlg m f a -> a -> m (g f) anaM f = fmap inf . (traverse (anaM f) =<<) . f {-| 'CataAlg' is a catamorphism F-Algebra. -} type CataAlg f a = f a -> a {-| 'CataMAlg' is a monadic catamorphism F-Algebra. -} type CataMAlg m f a = f a -> m a {-| 'cata' applies a 'CataAlg' over a fixed point of a 'Functor'. -} cata :: (Functor f, Fixed g) => CataAlg f a -> g f -> a {-# INLINE[1] cata #-} cata f = f . fmap (cata f) . outf {-| 'cataM' is a monadic catamorphism. -} cataM :: (Traversable f, Fixed g, Monad m) => CataMAlg m f a -> g f -> m a cataM f = (>>= f) . (traverse (cataM f)) . outf {-| 'ParaAlg' is a paramorphism F-Algebra. -} type ParaAlg g f a = f (g f, a) -> a {-| 'ParaAlg' is a monadic paramorphism F-Algebra. -} type ParaMAlg m g f a = f (g f, a) -> m a {-| 'para' applies a 'ParaAlg' over a fixed point of a 'Functor'. -} para :: (Functor f, Fixed g) => ParaAlg g f a -> g f -> a para f = f . fmap para' . outf where para' x = (x, para f x) {-| 'paraM' is a monadic paramorphism. -} paraM :: (Traversable f, Fixed g, Monad m) => ParaMAlg m g f a -> g f -> m a paraM f = (>>= f) . mapM para' . outf where para' x = do x' <- paraM f x return (x, x') {-| 'iso' maps from a fixed point of a 'Functor' to a different fixed point of the same 'Functor'. For any two well-behaved instances of 'Fixed', the shape of the 'Functor' should remain unchanged. -} iso :: (Functor f, Fixed g, Fixed h) => g f -> h f iso = cata inf {-| 'hylo' combines ana and cata into a single operation. -} hylo :: Functor f => AnaAlg f a -> CataAlg f b -> a -> b hylo f g = hylo' where hylo' = g . fmap hylo' . f {-# RULES "hylo" forall f g a. cata g (ana f a) = hylo f g a #-} {-| 'hyloM' is a monadic hylomorphism. -} hyloM :: (Traversable f, Monad m) => AnaMAlg m f a -> CataMAlg m f b -> a -> m b hyloM f g = hylo' where hylo' = g <=< mapM hylo' <=< f {-# RULES "hyloM" forall f g a. anaM f a >>= cataM g = hyloM f g a #-} {-| 'FixedAlg' is a typeclass for describing the relationship between a 'Functor' that is used with a 'Fixed' combinator and an algebraic datatype in that 'Functor' other than the one used for fixed-point recursion. -} class FixedAlg (f :: * -> *) where type Alg f :: * {-| 'FixedSub' is a typeclass for describing the relationship between a 'FixedAlg' 'Functor' @f@ and that same 'Functor' with @Alg f@ switched from @v@ to @v'@. -} class FixedAlg f => FixedSub f where type Sub f v v' :: * -> * {-| 'FixedFunctor' is a typeclass for describing mapping behavior for datatypes used with 'Fixed' combinators. -} class FixedSub f => FixedFunctor f where -- | Map over a 'Fixed' recursive 'FixedSub' @f@. fmapF :: (Fixed g, Fixed g', a ~ Alg f) => (a -> b) -> g f -> g' (Sub f a b) -- | 'fmapF', but using a single instance of 'Fixed'. fmapF' :: (FixedFunctor f, Fixed g, a ~ Alg f) => (a -> b) -> g f -> g (Sub f a b) fmapF' = fmapF {-| 'FixedFoldable' is a typeclass for describing folds over datatypes with 'Fixed' combinators. -} class FixedAlg f => FixedFoldable f where -- | Fold over a 'Fixed' recursive 'FixedAlg' @f@. foldMapF :: (Fixed g, Monoid m, a ~ Alg f) => (a -> m) -> g f -> m {-| 'FixedTraversable' is a typeclass for describing traversals over datatypes with 'Fixed' combinators. -} class FixedSub f => FixedTraversable f where -- | Traverse over a 'Fixed' recursive 'FixedSub' @f@ in the 'Applicative' -- @h@. traverseF :: (Fixed g, Fixed g', Applicative h, a ~ Alg f) => (a -> h b) -> g f -> h (g' (Sub f a b)) -- | 'traverseF', but using a single instance of 'Fixed'. traverseF' :: (FixedTraversable f, Fixed g, Applicative h, a ~ Alg f) => (a -> h b) -> g f -> h (g (Sub f a b)) traverseF' = traverseF