-- | Scary named folds...

{-# LANGUAGE CPP #-}
module Data.Generics.Fixplate.Morphisms where

--------------------------------------------------------------------------------

import Data.Foldable
import Data.Generics.Fixplate.Base

#ifdef WITH_QUICKCHECK
import Test.QuickCheck
import Data.Generics.Fixplate.Traversals
import Data.Generics.Fixplate.Test.Tools
#endif 

--------------------------------------------------------------------------------

-- | A /paramorphism/ is a generalized (right) fold.
para :: Functor f => (Mu f -> f a -> a) -> Mu f -> a
para h = go where
  go t = h t (fmap go $ unFix t) 

para' :: Functor f => (f (Mu f, a) -> a) -> Mu f -> a
para' h = go where
  go (Fix t) = h (fmap go' $ t)
  go' t = (t, go t) 

paraList :: (Functor f, Foldable f) => (Mu f -> [a] -> a) -> Mu f -> a 
paraList f = go where
  go t = f t (toList $ fmap go $ unFix t)

-- | A /catamorphism/ is a simpler version of a paramorphism
cata :: Functor f => (f a -> a) -> Mu f -> a
cata h = go where
  go = h . fmap go . unFix

-- | An /anamorphism/ is simply an unfold.
ana :: Functor f => (a -> f a) -> a -> Mu f
ana h = go where
  go = Fix . fmap go . h
  -- go x = Fix (fmap go (h x))

-- | A /hylomorphism/ is the composition of a catamorphism and an anamorphism.
hylo :: Functor f => (f a -> a) -> (b -> f b) -> (b -> a) 
hylo g h = cata g . ana h

--------------------------------------------------------------------------------